java - 我的线程无法唤醒

标签 java multithreading

已编辑:添加了答案中的解决方案。

因此,我构建了经典的多线程程序,其中 2 个线程(生产者和消费者)从同一个列表中读取数据。我确实通过让我的消费者线程不断轮询列表来使其正常工作,但这效率低下,并且给我带来了一些其他问题。所以我想我应该尝试等待并通知。我的两个线程在下面的代码中使用相同的缓冲区引用:

缓冲区:

public class Buffer {

    private Queue<Character> result;

    public Buffer() {
        System.out.println("Buffer");
        result = new LinkedList<Character>();
    }

    public void addChar(char c) {
        result.add(c);
    }

    public char readChar() {
        return (char) result.remove();
    }

    public boolean isEmpty() {
        return result.isEmpty();
    }

}

制作人

public class Producer implements Runnable {

private Buffer buffer;
private Callback callback;
private String input;

public Producer(Callback callback, Buffer buffer, String input) {
    this.callback = callback;
    this.buffer = buffer;
    this.input = input;
}

@Override
public void run() {
    System.out.println("producer started");
       synchronized (buffer) {
            char[] array = input.toCharArray();
            for (int i = 0; i < array.length; i++) {
                buffer.addChar(array[i]);
                System.out.println("Adding--------- data");
                callback.returnData("Added " + array[i]);
                buffer.notifyAll();
            }
        }
    }
}

消费者

public class Consumer implements Runnable {

    private Callback callback;
    private Buffer buffer;

    public Consumer(Callback callback, Buffer buffer) {
        this.callback = callback;
        this.buffer = buffer;
    }

    @Override
public void run() {
    System.out.println("consumer started");
    try {
        synchronized (buffer) {
            while (true) {
                if (buffer.isEmpty()) {
                    System.out.println("Queue is empty ");
                    buffer.wait();
                }
                if (!buffer.isEmpty()) {
                    System.out.println("Reading data");
                    callback.returnData(" Retrieved " + String.valueOf(buffer.readChar()));
                } else {
                    callback.returnData("Waiting");
                }
            }
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
}

我想要做的是每次生产者线程添加一个元素时,它应该通知我的消费者线程现在可以从缓冲区读取。问题是,直到所有数据都添加完毕后,它才会被唤醒。这是我从控制台得到的结果:

buffer initiated
producer started
consumer started
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Adding--------- data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Reading data
Queue is empty 

现在,我真正想要的是:“添加数据”、“读取数据”、“添加数据”等等。

最佳答案

同步 block 与您希望等待通知的同一对象一起使用。只需将 synchronized 关键字放入方法签名中,您就可以锁定 this (当前对象),但当您调用 waitnotify 时缓冲区上,因此您需要在缓冲区上进行同步 block 。

您应该如下所示放入方法 block 中,并从methid签名中删除synchronized关键字。

synchronized (buffer) {
    // call notify ,( prefer notifyAll.) on buffer
}

和类似的等待 block 。

以下是您可以尝试的更改,

public class Producer implements Runnable {
    private Buffer buffer;
    private Callback callback;
    private String input;

    public Producer(Callback callback, Buffer buffer, String input) {
        this.callback = callback;
        this.buffer = buffer;
        this.input = input;
    }

    @Override
    public void run() {
        synchronized (buffer) {
            char[] array = input.toCharArray();
            try {
                for (int i = 0; i < array.length; i++) {
                    buffer.addChar(array[i]);   // production done, 
                    // 1. now notify the consumer thread and 
                    // 2. wait till consumer consumes and notifies.
                    System.out.println("Adding--------- data");
                    callback.returnData("Added " + array[i]);
                    buffer.notifyAll();         // 1. notify done [ consumer notified , this will make consumer get awake from wait state and try to re aquire the lock]
                    buffer.wait();          //2. waiting for notification from consumer thread.
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class Consumer implements Runnable {

    private Callback callback;
    private Buffer buffer;

    public Consumer(Callback callback, Buffer buffer) {
        this.callback = callback;
        this.buffer = buffer;
    }

    @Override
    public void run() {
        try { // having try catch outside will enable you to call interrupt on
                // the Thread to stop it
            synchronized (buffer) {
                // though we check for interrupt flag but in case thread is
                // interrupted while waiting, and InterruptedException is
                // thrown, the flag will be cleared, hence we need to put try
                // catch block outside, other option is to put it inside and make thread true again from catch block.
                while (!Thread.currentThread().isInterrupted()) {
                    if (!buffer.isEmpty()) { 
                        System.out.println("Reading data");
                        callback.returnData(" Retrieved " + String.valueOf(buffer.readChar())); // consumption done, now
                                                                                                // 1. Notify the consumer thread
                                                                                                // 2. Wait till more production is done
                        buffer.notifyAll(); // 1. Consumer thread will get this notification and it will get in awakened state. It will try to aquire the lock on Buffer object.
                    } else {
                        System.out.println("Queue is empty ");
                        callback.returnData("Waiting");
                    }
                    buffer.wait();      // 2. wait until more production is done and producer notifies after production
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

关于java - 我的线程无法唤醒,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40617041/

相关文章:

android - 为什么非UI线程可以修改UI?

java - java中如何测量图像上绘制的线的长度?

java - 如何在 Jython 中安装各种 Python 库?

Java TimerTask 具有时间、延迟和周期

java - 如何在 JPA 中使用 Enum 作为 NamedQuery 参数

java - 如何将字符串中的 11 个数字选择到数组中?

c++ - 比较是原子操作吗?

c# - 无法将类型 'System.Threading.Tasks.Task' 隐式转换为 'Windows.Foundation.IAsyncAction' 。存在显式转换

c++ - 终止使用 QueueUserWorkItem(win 32/nt5) 创建的线程池中长时间运行的线程

android - 为什么 Handler::postDelayed 会使 UI 卡住