java - 如何在调用通知之前确保所有线程都已完成?
问题描述
我试图确保在调用 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();
}
}
解决方案
我会在一个允许线程安全更新列表的类中扭曲列表:
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 ,如此处所示。
推荐阅读
- javascript - 在 Vue (CKEditor) 中访问组件的实例
- css - flex-child 内 iframe 上的 height="100%" 在 Safari 中不起作用
- iis - 现有 IIS 10 ASP.NET MVC 站点上安装的 Asp.net 核心?
- elasticsearch - 当我们可以在弹性搜索中保存文档时,为什么还需要另一个 Nosql
- makefile - 如何导入 yaml 文件以在 gnu make 文件中使用它?
- c# - 用于字符串比较的 Roslyn Analyzer
- android - Flutter Dynamic Icons 未在生产版本中显示
- javascript - LeafletJS 工具提示偏移量取决于 properties.name
- reactjs - 使用 LocalStorage 反应钩子
- ios - 如何在不阻塞主线程的情况下渲染 UIView?