【背景】今天遇到一个并发问题,为了在开发环境复现这个bug,需要让多个线程同时执行到某条语句。
【解决方案】
java1.5的concurrent包下的CyclicBarrier 和 CountDownLatch都能解决这个问题。不得不佩服Doug Lea!
【方法1】使用CyclicBarrier
public class TestCyclicBarrier {
class Worker implements Runnable{
CyclicBarrier cyclicBarrier;
public Worker(CyclicBarrier cyclicBarrier){
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
try {
cyclicBarrier.await(); // 等待其它线程
System.out.println(Thread.currentThread().getName() + "启动@" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
public void doTest() throws InterruptedException {
final int N = 5; // 线程数
CyclicBarrier cyclicBarrier = new CyclicBarrier(N);
for(int i=0;i<N;i++){
new Thread(new Worker(cyclicBarrier)).start();
}
}
public static void main(String[] args) throws InterruptedException {
TestCyclicBarrier testCyclicBarrier = new TestCyclicBarrier();
testCyclicBarrier.doTest();
}
}
【方法2】使用CountDownLatch
package thread;
import java.util.concurrent.CountDownLatch;
public class TestCountDownLatch {
class Worker implements Runnable{
CountDownLatch countDownLatch;
Worker(CountDownLatch countDownLatch){
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
try {
countDownLatch.await(); // 等待其它线程
System.out.println(Thread.currentThread().getName() + "启动@" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void doTest() throws InterruptedException {
final int N = 5; // 线程数
CountDownLatch countDownLatch = new CountDownLatch(N);
for(int i=0;i<N;i++){
new Thread(new Worker(countDownLatch)).start();
countDownLatch.countDown();
}
}
public static void main(String[] args) throws InterruptedException {
TestCountDownLatch testCountDownLatch = new TestCountDownLatch();
testCountDownLatch.doTest();
}
}
这里记录一下下面这种情况:主线程需要等待多个子线程执行完后再执行。
我们先看一下下面的场景:
package com.java4all.mypoint;
import java.util.concurrent.CountDownLatch;
/**
* Author: yunqing
* Date: 2018/7/18
* Description:线程测试
* 测试点:主线程等待子线程全部执行完后再执行
*/
public class ThreadTest {
public static void main(String[] args)throws Exception{
System.out.println("主线程正在执行前:"+Thread.currentThread().getName());
test3();
System.out.println("主线程正在执行后:"+Thread.currentThread().getName());
}
public static void test3(){
try {
for (int i = 1 ;i <= 10;i ++){
Thread.sleep(1000);
new Thread(()->{
System.out.println("子线程正在执行:"+Thread.currentThread().getName());
}).start();
}
}catch (Exception ex){
ex.printStackTrace();
}
}
}
执行结果为:
主线程正在执行前:main
子线程正在执行:Thread-0
子线程正在执行:Thread-1
子线程正在执行:Thread-2
子线程正在执行:Thread-3
子线程正在执行:Thread-4
子线程正在执行:Thread-5
子线程正在执行:Thread-6
子线程正在执行:Thread-7
子线程正在执行:Thread-8
主线程正在执行后:main
子线程正在执行:Thread-9
可以看到,子线程还没执行完时,主线程进来了。
1.使用CountDownLatch
示例如下,我们初始化一个CountDownLatch,值为10(子线程个数),然后每次一个子线程执行完后执行一下countDown(),代码示例如下:
package com.java4all.mypoint;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Author: yunqing
* Date: 2018/7/18
* Description:线程测试
* 测试点:主线程等待子线程全部执行完后再执行
*/
public class ThreadTest {
/**初始化CountDownLatch,值为线程数量*/
private static final CountDownLatch ctl = new CountDownLatch(10);
public static void main(String[] args)throws Exception{
System.out.println("主线程正在执行前:"+Thread.currentThread().getName());
test3();
ctl.await(20, TimeUnit.SECONDS);//最多等待20秒,不管子线程完没完
System.out.println("主线程正在执行后:"+Thread.currentThread().getName());
}
public static void test3(){
try {
for (int i = 1 ;i <= 10;i ++){
Thread.sleep(1000);
new Thread(()->{
System.out.println("子线程正在执行:"+Thread.currentThread().getName());
}).start();
ctl.countDown();
}
}catch (Exception ex){
ex.printStackTrace();
}
}
}
执行结果为:
主线程正在执行前:main
子线程正在执行:Thread-0
子线程正在执行:Thread-1
子线程正在执行:Thread-2
子线程正在执行:Thread-3
子线程正在执行:Thread-4
子线程正在执行:Thread-5
子线程正在执行:Thread-6
子线程正在执行:Thread-7
子线程正在执行:Thread-8
子线程正在执行:Thread-9
主线程正在执行后:main
或者用java8之前的方式写:
线程类:
package com.java4all.mypoint;
import java.util.concurrent.CountDownLatch;
/**
* Author: yunqing
* Date: 2018/7/23
* Description:
*/
public class MyRunnable implements Runnable{
public CountDownLatch countDownLatch;
@Override
public void run() {
try {
Thread.sleep(2000);
System.out.println("子线程正在执行任务,当前线程为:"+Thread.currentThread().getName());
}catch (InterruptedException inex){
inex.printStackTrace();
}finally {
countDownLatch.countDown();
}
}
public CountDownLatch getCountDownLatch() {
return countDownLatch;
}
public void setCountDownLatch(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
}
测试类:
package com.java4all.mypoint;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Author: yunqing
* Date: 2018/7/18
* Description:线程测试
* 测试点:主线程等待子线程全部执行完后再执行
*/
public class ThreadTest {
/**初始化CountDownLatch,值为线程数量*/
private static final CountDownLatch ctl = new CountDownLatch(10);
public static void main(String[] args)throws Exception{
System.out.println("主线程正在执行前:"+Thread.currentThread().getName());
for(int i = 1;i <= 10;i ++){
MyRunnable runnable = new MyRunnable();
runnable.setCountDownLatch(ctl);
Thread thread = new Thread(runnable);
thread.start();
}
ctl.await(20, TimeUnit.SECONDS);//最多等待20秒,不管子线程完没完
System.out.println("主线程正在执行后:"+Thread.currentThread().getName());
}
}
结果为:
主线程正在执行前:main
子线程正在执行任务,当前线程为:Thread-1
子线程正在执行任务,当前线程为:Thread-0
子线程正在执行任务,当前线程为:Thread-2
子线程正在执行任务,当前线程为:Thread-3
子线程正在执行任务,当前线程为:Thread-4
子线程正在执行任务,当前线程为:Thread-7
子线程正在执行任务,当前线程为:Thread-6
子线程正在执行任务,当前线程为:Thread-5
子线程正在执行任务,当前线程为:Thread-9
子线程正在执行任务,当前线程为:Thread-8
主线程正在执行后:main
附:
开启一个线程的其他写法:
/**jdk7匿名内部类的写法*/
public static void test1(){
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("aaaa");
}
}).start();
}
/**
* jdk8
* Runnable是个函数接口,可以利用jdk8的lambda来简写
* 函数接口:是指内部只有一个抽象方法的接口
* */
public static void test2(){
new Thread(()->{
System.out.println("bbb");
}).start();
}