java并发编程学习:如何等待多个线程执行完成后再继续后续处理(synchronized、join、FutureTask、CyclicBarrier)
多线程应用中,经常会遇到这种场景:后面的处理,依赖前面的N个线程的处理结果,必须等前面的线程执行完毕后,后面的代码才允许执行。
在我不知道CyclicBarrier之前,最容易想到的就是放置一个公用的static变量,假如有10个线程,每个线程处理完上去累加下结果,然后后面用一个死循环(或类似线程阻塞的方法),去数这个结果,达到10个,说明大家都爽完了,可以进行后续的事情了,这个想法虽然土鳖,但是基本上跟语言无关,几乎所有主流编程语言都支持。
package yjmyzz.test; public class ThreadLockTest { public static int flag = 0;//公用变量 public static void main(String[] args) throws Exception {
ThreadLockTest testObj = new ThreadLockTest();
final int threadNum = 10; for (int i = 0; i < threadNum; i++) {
new Thread(new MyRunable(i, testObj)).start();
} while (true) {
if (testObj.flag >= threadNum) {
System.out.println("-----------\n所有thread执行完成!");
break;
}
Thread.sleep(10);
}
} static class MyRunable implements Runnable {
int _i = 0;
ThreadLockTest _test; public MyRunable(int i, ThreadLockTest test) {
this._i = i;
this._test = test;
} @Override
public void run() {
try {
Thread.sleep((long) (Math.random() * 10));
System.out.println("thread " + _i + " done");
//利用synchronized获得同步锁
synchronized (_test) {
_test.flag += 1;
}
System.out.println("thread " + _i + " => " + _test.flag);//测试用
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
输出结果:
thread 0 done
thread 0 => 1
thread 9 done
thread 9 => 2
thread 1 done
thread 1 => 3
thread 3 done
thread 3 => 4
thread 7 done
thread 7 => 5
thread 6 done
thread 6 => 6
thread 2 done
thread 2 => 7
thread 4 done
thread 4 => 8
thread 8 done
thread 8 => 9
thread 5 done
thread 5 => 10
-----------
所有thread执行完成!
除了这个方法,还可以借助FutureTask,达到类似的效果,其get方法会阻塞线程,等到该异步处理完成。缺点就是,FutureTask调用的是Callable,必须要有返回值,所以就算你不想要返回值,也得返回点啥
package yjmyzz.test; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask; public class FutureTaskTest { public static void main(String[] args) throws ExecutionException, InterruptedException { FutureTask<String>[] tasks = new FutureTask[10]; for (int i = 0; i < tasks.length; i++) {
final int j = i;
tasks[i] = new FutureTask<String>(new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep((long) (Math.random() * 100));
return "task" + j + " done";
}
});
new Thread(tasks[i]).start();
} for (int i = 0; i < tasks.length; i++) {
System.out.println(tasks[i].get());//依次等待所有task执行完毕
} System.out.println("-----------\n所有task执行完成!"); }
}
执行结果:
task0 done
task1 done
task2 done
task3 done
task4 done
task5 done
task6 done
task7 done
task8 done
task9 done
-----------
所有task执行完成!
此外,Thread的Join方法也可以实现类似的效果,主要代码如下:
public static void main(String[] args) throws Exception { final int threadNum = 10;
Thread[] threads = new Thread[threadNum]; for (int i = 0; i < threadNum; i++) {
threads[i] = new Thread(new MyRunable(i));
threads[i].start();
} for (int i = 0; i < threadNum; i++) {
threads[i].join();
} System.out.println("-----------\n所有thread执行完成!"); }
当然,这个需求最“正统”的解法应该是使用CyclicBarrier,它可以设置一个所谓的“屏障点”(或称集合点),好比在一项团队活动中,每个人都是一个线程,但是规定某一项任务开始前,所有人必须先到达集合点,集合完成后,才能继续后面的任务。
package yjmyzz.test; import java.util.concurrent.CyclicBarrier; public class ThreadTest { public static void main(String[] args) throws Exception { final int threadNum = 10;
CyclicBarrier cb = new CyclicBarrier(threadNum + 1);//注意:10个子线程 + 1个主线程 for (int i = 0; i < threadNum; i++) {
new Thread(new MyRunable(cb, i)).start();
} cb.await();
System.out.println("-----------\n所有thread执行完成!");
} static class MyRunable implements Runnable {
CyclicBarrier _cb;
int _i = 0; public MyRunable(CyclicBarrier cb, int i) {
this._cb = cb;
this._i = i;
} @Override
public void run() {
try {
Thread.sleep((long) (Math.random() * 100));
System.out.println("thread " + _i + " done,正在等候其它线程完成...");
_cb.await();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
thread 9 done,正在等候其它线程完成...
thread 5 done,正在等候其它线程完成...
thread 0 done,正在等候其它线程完成...
thread 6 done,正在等候其它线程完成...
thread 4 done,正在等候其它线程完成...
thread 2 done,正在等候其它线程完成...
thread 3 done,正在等候其它线程完成...
thread 8 done,正在等候其它线程完成...
thread 7 done,正在等候其它线程完成...
thread 1 done,正在等候其它线程完成...
-----------
所有thread执行完成!
参考文章:
http://ifeve.com/concurrency-cyclicbarrier/
http://ifeve.com/thread-synchronization-utilities-5/
http://ifeve.com/semaphore-countdownlatch-cyclicbarrier-phaser-exchanger-in-java/
http://ifeve.com/thread-management-7/
java并发编程学习:如何等待多个线程执行完成后再继续后续处理(synchronized、join、FutureTask、CyclicBarrier)的更多相关文章
- Java并发编程学习笔记(一)——线程安全性
主要概念:线程安全性.原子性.原子变量.原子操作.竟态条件.复合操作.加锁机制.重入.活跃性与性能. 1.当多个线程访问某个状态变量并且其中有一个线程执行写入操作时,必须采用同步机制来协同这些线程对变 ...
- Java并发编程学习笔记
Java编程思想,并发编程学习笔记. 一.基本的线程机制 1.定义任务:Runnable接口 线程可以驱动任务,因此需要一种描述任务的方式,这可以由Runnable接口来提供.要想定义任务,只需实现R ...
- Java并发编程学习前期知识下篇
Java并发编程学习前期知识下篇 通过上一篇<Java并发编程学习前期知识上篇>我们知道了在Java并发中的可见性是什么?volatile的定义以及JMM的定义.我们先来看看几个大厂真实的 ...
- 并发编程学习笔记(14)----ThreadPoolExecutor(线程池)的使用及原理
1. 概述 1.1 什么是线程池 与jdbc连接池类似,在创建线程池或销毁线程时,会消耗大量的系统资源,因此在java中提出了线程池的概念,预先创建好固定数量的线程,当有任务需要线程去执行时,不用再去 ...
- Java并发编程(二)如何保证线程同时/交替执行
第一篇文章中,我用如何保证线程顺序执行的例子作为Java并发系列的开胃菜.本篇我们依然不会有源码分析,而是用另外两个多线程的例子来引出Java.util.concurrent中的几个并发工具的用法. ...
- 学习笔记:java并发编程学习之初识Concurrent
一.初识Concurrent 第一次看见concurrent的使用是在同事写的一个抽取系统代码里,当时这部分代码没有完成,有许多的问题,另一个同事接手了这部分代码的功能开发,由于他没有多线程开发的经验 ...
- Java并发编程学习路线(转)
以前特地学过并发编程,但是没怎么学进去,不太喜欢.最近发现,作为一个资深工程师,却没有完整深入系统的学习过,而反是现在的BAT大并发是必须的,感觉甚是惭愧. 故找了一片学习文章,如下,准备集中一段时间 ...
- Java并发编程学习路线
一年前由于工作需要从微软技术栈入坑Java,并陆陆续续做了一个Java后台项目,目前在搞Scala+Java混合的后台开发,一直觉得并发编程是所有后台工程师的基本功,所以也学习了小一年Java的并发工 ...
- Java并发指南开篇:Java并发编程学习大纲
Java并发编程一直是Java程序员必须懂但又是很难懂的技术内容. 这里不仅仅是指使用简单的多线程编程,或者使用juc的某个类.当然这些都是并发编程的基本知识,除了使用这些工具以外,Java并发编程中 ...
随机推荐
- 原生HTML5 input type=file按钮UI自定义
原生<input type="file" name="file" />长得太丑 提升一下颜值 实现方案一.设置input[type=file]透明度 ...
- 浅谈HTML5单页面架构(二)——backbone + requirejs + zepto + underscore
本文转载自:http://www.cnblogs.com/kenkofox/p/4648472.html 上一篇<浅谈HTML5单页面架构(一)--requirejs + angular + a ...
- javascript中的this和e.target的深入研究
this 是javascript的一个关键字,当函数运行时在内部自动生成.this是会变化的,在不同的场合,代表的东西就不一样.简单点来说,this指调用这个函数的对象.当你使用this代表的当前ht ...
- 阻止默认事件event.preventDefault();
阻止浏览器默认事件.什么是默认事件,例如浏览器默认右键菜单.a标签默认连接跳转...,如何阻止呢? Firefox中,event必须作为参数传入. IE中,event是window对象的属性. ev ...
- Sharepoint学习笔记—习题系列--70-573习题解析 -(Q136-Q138)
Question 136You need to create a custom content type and specify the content type ID.What should you ...
- Shou 团队诚意满满的招募 Swifter
一.团队介绍 团队产品 VPlayer 播放器靠自增长 3 年内获得全球 4000 万用户,开发的 Vitamio 组件更是获得微博.UC.金山等知名企业授权使用.—— 团队再次起航,经历一年多我们已 ...
- RoboGuice 3.0 (二)进阶篇
上篇介绍了RoboGuice的接入及基本使用,其中涉及到了一个@Singleton和@ContextSingleton的注解,这些都是作用域的注解,这篇我们先说明有关作用域的问题. 一.作用域 Sco ...
- iOS--xuer(registration)
这个登录页面包含了自适应屏幕的大小,数字用户登录键盘是数字键盘.隐藏键盘.隐藏密码等等. ViewController.h #import <UIKit/UIKit.h> #import ...
- 利用split
java.lang.string.splitsplit 方法将一个字符串分割为子字符串,然后将结果作为字符串数组返回.stringObj.split([separator,[limit]])strin ...
- #VSTS日志# TFS 2015 Update 2 RC2新功能
有段时间没有更新#VSTS日志#了,最近小编太忙,全国各地飞来飞去给各种不同的团队实施敏捷,今天冷不丁一看,呀!TFS 2015 Update 2 RC2都已经发布了.里面好东西不少,列出几个给大家瞧 ...