java - 如何在调用notify之前确保所有线程都已完成?

标签 java multithreading thread-safety

我试图确保 A 类的所有线程在调用通知之前都已完成。此时,如果一个线程完成了工作,他会调用notify(B 类的线程),而其他A 线程仍在运行。如果发生这种情况,B 线程开始工作,这会改变剩余 A 线程的条件。

我尝试过使用同步块(synchronized block),但我猜我用错了。

这个想法是,A 填充数组直到填满并通知 B,以便它可以再次清空数组。

public class A extends Thread {

    public static Object lockA = new Object();

    private ArrayList<String> list;

    public A(ArrayList<String> list) {
        this.list = list;
    }

    public void run(){

        while(true){
            synchronized (A.lockA){
                if(list.size() < 10){
                    list.add("A");
                    System.out.println(currentThread().getName() + " " + list);
                }else{
                    synchronized (B.lockB){
                        B.lockB.notifyAll();

                    }
                    return;
                }
            }

        }
    }

}
public class B extends Thread {

    public static Object lockB = new Object();

    private ArrayList<String> list;

    public B(ArrayList<String> list) {
        this.list = list;
    }

    public void run(){


            synchronized (B.lockB){
                try {
                    B.lockB.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                while (list.size() > 0){
                    list.remove("A");
                    System.out.println(currentThread().getName() + " " + list);
                }
                return;
            }
    }
}
public class Main {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();

        A a = new A(list);
        A aa = new A(list);
        A aaa = new A(list);
        B b = new B(list);
        B bb = new B(list);
        B bbb = new B(list);
        B bbbb = new B(list);

        a.start();
        aa.start();
        aaa.start();
        b.start();
        bb.start();
        bbb.start();
        bbbb.start();
    }
}

最佳答案

除非lockB保护线程可能想要等待更改或可能需要通知另一个线程有关更改的某些状态,否则将其与wait结合使用,并且通知没有任何意义。

        synchronized (B.lockB){
            try {
                B.lockB.wait();

在检查并确保您正在等待的事情尚未发生之前,您绝不能调用wait。您将此代码放在 synchronized block 中的原因正是为了当且仅当您需要等待时才可以执行该检查并等待。

                synchronized (B.lockB){
                    B.lockB.notifyAll();
                }

这真是令人费解。如果您没有更改受该锁保护且需要通知其他线程的任何内容,为什么还要调用 lockB.notifyAll()

看起来您并没有真正理解 wait/notify 语义的实际工作原理。关键是,只有在某些事情还没有发生的情况下,您才需要等待,并且在不持有某些锁的情况下,您永远无法确定它是否已经发生(因为调度程序是不可预测的)。但是您需要在等待之前释放该锁,如果其他线程在您释放锁之后但在您开始等待之前执行您正在等待的操作,则会创建竞争条件。 wait 函数是一个原子函数,用于释放锁并等待某些事情发生。原子性避免了这种竞争条件。

但是,您没有释放保护您正在等待的内容的锁。这破坏了原子解锁和等待函数的逻辑,并导致代码总是死锁。

关于java - 如何在调用notify之前确保所有线程都已完成?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55307092/

相关文章:

java - 我没有收到此类元素异常 : No file found while running my project

不影响性能的Java线程

java - 套接字上并发读写的线程安全

c++ - pthread 在预期之后执行

multithreading - 读/写模块数组的不同元素是线程安全的吗?

java - 线程管理/spring @Async 的工作

c++ - C++中的静态初始化和线程安全

java - 让 log4j2 与 eclipse 一起工作

java - "Exception in thread "主要"java.lang.ArrayIndexOutOfBoundsException: 0"java错误

java - Titan + Tinkerpop 读取速度极慢