在订阅者线程中调用Thread.Sleep会导致发布者线程进入睡眠状态

时间:2018-07-26 06:09:33

标签: java multithreading design-patterns

我已经在我的应用程序中实现了发布和订阅模式,但是当我在任何一个订阅者中调用Thread.sleep()方法或我的任何一个订阅者抛出异常时,所有其他订阅者和发布者都会受到此影响,因此如何防止这是发生的。

我已经创建了一个有关上述问题的小演示

发布者代码

import java.util.Random;

public class Publisher extends Thread {

    Broker broker = Broker.getInstance();
    Random random = new Random();

    @Override
    public void run() {
        while (true) {
            System.out.println("Published " + new Timestamp(System.currentTimeMillis()));
            broker.updateSubscribers(Integer.toString(random.nextInt(250)));
        }

    }
}

用户界面

public interface Subscriber {

    public void onUpdate(String message);
}

MessageSubscriber代码

import java.util.logging.Level;
import java.util.logging.Logger;

public class MessageSubscriber extends Thread implements Subscriber {

    Broker broker = Broker.getInstance();

    @Override
    public void run() {
        System.out.println("MessageSubscriber started...");
        broker.subscribe(this);
    }

    @Override
    public void onUpdate(String message) {
        try {
            System.out.println(message);
            sleep(1000);                    // called sleep affects the publisher too
        } catch (InterruptedException ex) {
            Logger.getLogger(MessageSubscriber.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

}

如您所见,我在MessageSubscriber中调用了sleep方法,这也影响了发布服务器并使它在该时间内也保持睡眠状态

编辑添加的经纪人代码

import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author hemants
 */
public class Broker {

    List<Subscriber> subscribersList = new ArrayList<>();

    private Broker() {
    }

    public static Broker getInstance() {
        return BrokerHolder.INSTANCE;
    }

    private static class BrokerHolder {

        private static final Broker INSTANCE = new Broker();
    }

    public void subscribe(Subscriber s) {
        subscribersList.add(s);
    }

    public void unsubscribe(Subscriber s) {
        subscribersList.remove(s);
    }

    public void updateSubscribers(String message) {
        subscribersList.stream().forEach(subscriber -> subscriber.onUpdate(message));
    }
}

运行上述代码的主类

public class PubSubPattern {

    public static void main(String[] args) {
        Publisher publisher = new Publisher();
        publisher.start();

        MessageSubscriber messageSubscriber = new MessageSubscriber();
        messageSubscriber.start();
    }
}

好吧,我已经编辑了MessageSubscribe代码,如下所示,它的工作符合我的预期

import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author hemants
 */
public class MessageSubscriber extends Thread implements Subscriber {

    Broker broker = Broker.getInstance();

    @Override
    public void run() {
        System.out.println("MessageSubscriber started...");

        while (true) {
            try {
                broker.subscribe(this);
                System.out.println("subscribed ");
                sleep(1000);
                broker.unsubscribe(this);
                System.out.println("un subscribed");
                sleep(1000);
            } catch (InterruptedException ex) {
                Logger.getLogger(MessageSubscriber.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    @Override
    public void onUpdate(String message) {
        System.out.println(message);
    }

}

你怎么说?

3 个答案:

答案 0 :(得分:1)

所以您执行这样的事情

subscribersList.stream().forEach(subscriber -> subscriber.onUpdate(message));

onUpdate期间,您sleep

因此有效

subscribersList.stream().forEach(subscriber -> Thread.sleep());

或更详细

for(Subscriber sub:subscribers){
   Thread.sleep(xxx);
}

难怪它会“影响”其他侦听器,因为此处阻止了呼叫者。调用者线程在每个元素上睡眠。

要么使用线程池并提交更新任务,要么使用subscribersList.parallelStream()

我希望这只是出于教育目的。

答案 1 :(得分:0)

您正在同一线程中更新订户,这就是为什么它将影响其他订户的原因。并阻止发布者。

创建新线程以更新代理,就可以了。

答案 2 :(得分:0)

这是一个快速的解决方案。我更新了MessageSubscriber,将接口Subscriber的用法保留在Broker中:

public class MessageSubscriber extends Thread implements Subscriber {

    Broker broker = Broker.getInstance();

    @Override
    public void run() {
        System.out.println("MessageSubscriber started...");
        synchronized (broker) {
            broker.subscribe(this);
        }
        try {
            synchronized (this) {
                wait();
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            // TODO OP has to decide how to handle this
            // for example
            synchronized (broker) {
                broker.unsubscribe(this);
            }
        }
    }

    @Override
    public void onUpdate(String message) {
        try {
            synchronized (this) {
                notify();
            }
            System.out.println(message);
            sleep(1000); // called sleep affects the publisher too
        } catch (InterruptedException ex) {
            Thread.currentThread().interrupt();
            Logger.getLogger(MessageSubscriber.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

}

我不确定InterruptedExceptionrun()的处理方式,因为必须输入boker的锁才能进入同步块。因此,线程可能会等待此锁定,而不是有效地中断自身。