首页 > 解决方案 > 如何在调用通知之前确保所有线程都已完成?

问题描述

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

我尝试过使用同步块,但我猜我用错了。

这个想法是 A 填充 Array 直到其满并通知 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();
    }
}

标签: javamultithreadingthread-safety

解决方案


我会在一个允许线程安全更新列表的类中扭曲列表:

class ListWraper {
    //See also https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#synchronizedList-java.util.List-
    private volatile List<String> list;

    ListWraper() {
        list = new ArrayList<>();
    }

    //add to list
    synchronized boolean add(String s){
        return list.add(s);
    }

    //remove from list
    synchronized boolean remove(String s){
        return list.remove(s);
    }


    //get a defensive copy of list
    List<String> getList() {
        return new ArrayList<>(list);
    }
}

使用的线程ListWarper

class ListUpdater extends Thread {

    private static String abc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static int threadsCouter = 0;             //used for
    private final int threadNumber = threadsCouter++; //printing only
    private final Random rd = new Random();
    private final ListWraper list;

    ListUpdater(ListWraper list) {
        this.list = list;
    }

    @Override
    public void run(){
         for(int i = 0; i < 10; i++){
             //add random character to list 
             list.add(String.valueOf( abc.charAt(rd.nextInt(abc.length()))));
             try {
                TimeUnit.MILLISECONDS.sleep(100 + rd.nextInt(500)); //simulate long process 
            } catch (InterruptedException ex) { ex.printStackTrace();   }
         }
         System.out.println("Thread number " + threadNumber + " finished");
    }
}

并使用以下方法对其进行测试:

public class Main{

    public static void main(String args[]) throws InterruptedException {
        ListWraper list = new ListWraper();
        ListUpdater updater1 = new ListUpdater(list);
        ListUpdater updater2 = new ListUpdater(list);
        updater1.start();
        updater2.start();
        updater1.join(); //wait for thread to finish 
        updater2.join(); //wait for thread to finish 
        System.out.println("All finished ");
    }
}

输出:

线程数 1 完成
线程数 0 完成
全部完成

可以在此处找到完整的可运行代码。

另一个简单的替代方法是使用共享的CountDownLatch ,如此所示。


推荐阅读