CountDownLatch


CountDownLatch是当所有的线程执行完毕,也就是run方法体执行完了。再来执行另外一个线程。它是以计数器递减的方式来确定是否
所有的线程都执行完成了。所有的线程执行完成之后,计数器为0。

例子一


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package com.kongzhong.gw2.ccc.service;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.CountDownLatch;
/**
* CountDownLatch用法
* 等待所有的线程完成之后,再来完成另外一个线程.
* 张三和李四两个人同时开始工作。当两个人都完成了工作之后打印出一句话:“所有工作都完成了在sdf.format(new Date())”
* @author Mobim-Client
*
*/
public class TestCountDownLatch {

final static SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

public static void main(String[] args) throws InterruptedException {
CountDownLatch latch=new CountDownLatch(2);//两个工人的协作
Worker worker1=new Worker("张三", 5000, latch);
Worker worker2=new Worker("李四", 8000, latch);
worker1.start();
worker2.start();

latch.await();//等待所有工人完成工作

System.out.println("所有工作都完成在 "+sdf.format(new Date()));
}


static class Worker extends Thread{
String workerName;
int workTime;
CountDownLatch latch;
public Worker(String workerName ,int workTime ,CountDownLatch latch){
this.workerName=workerName;
this.workTime=workTime;
this.latch=latch;
}
public void run(){
System.out.println("工人: "+workerName+" 开始工作在 "+sdf.format(new Date()));
doWork();//在工作中....
System.out.println("工人: "+workerName+" 工作完成在 "+sdf.format(new Date()));
latch.countDown();//工人完成工作,计数器减一

}

private void doWork(){
try {
Thread.sleep(workTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

执行结果为:

1
2
3
4
5
工人: 张三 开始工作在 2016-05-21 14:20:02
工人: 李四 开始工作在 2016-05-21 14:20:02
工人: 张三 工作完成在 2016-05-21 14:20:07
工人: 李四 工作完成在 2016-05-21 14:20:10
所有工作都完成在 2016-05-21 14:20:10

例子二


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.kongzhong.gw2.ccc.service;

import java.util.concurrent.CountDownLatch;
/**
* 共10个线程,每一个线程打印出1,2。当10个线程完成后,再打印出"OK"
* @author Mobim-Client
*
*/
public class Test {

public static void main(String[] args) throws InterruptedException {
CountDownLatch latch=new CountDownLatch(10);
for(int i=1;i<=10;i++){
new Print(latch).start();
}
latch.await();//等待所有的线程执行完成
System.out.println("OK");
}
}
class Print extends Thread{
CountDownLatch latch;
public Print(CountDownLatch latch) {
this.latch=latch;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"---1");
System.out.println(Thread.currentThread().getName()+"---2");
latch.countDown();//计数器减一
}
}

执行结果为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Thread-0---1
Thread-2---1
Thread-1---1
Thread-3---1
Thread-2---2
Thread-4---1
Thread-0---2
Thread-4---2
Thread-3---2
Thread-7---1
Thread-7---2
Thread-1---2
Thread-6---1
Thread-6---2
Thread-5---1
Thread-5---2
Thread-9---1
Thread-8---1
Thread-8---2
Thread-9---2
OK

CyclicBarrier


CyclicBarrier是所有的线程在run方法中的await中阻塞了。当所有的线程都执行到await时才解除阻塞,继续执行run方法体中未执行的部分
CyclicBarrier是以计数的方式来确定是否都执行到了await,每一个线程执行到了await时计数器会加1。一直加到构造方法中给定的值。然后
就解除阻塞。计算器又回到0,所有CyclicBarrier是可以重用的。

例子


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
* 赛跑时,等待所有人都准备好时,才起跑:
* @author Mobim-Client
*
*/
public class Test {

public static void main(String[] args) throws InterruptedException {
CyclicBarrier barrier = new CyclicBarrier(3); //如果把3改成4,则该程序就一直等下去。
new Runner(barrier, "刘翔").start();
new Runner(barrier, "姚明").start();
new Runner(barrier, "潘晓婷").start();

}
}
class Runner extends Thread{
CyclicBarrier barrier;
String name;
public Runner(CyclicBarrier barrier,String name) {
this.barrier=barrier;
this.name=name;
}
@Override
public void run() {
try {
Thread.sleep(1000 * (new Random()).nextInt(8));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("选手:"+name+" 准备好了");
try {
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(name + " 起跑!");
}
}

运行结果:

1
2
3
4
5
6
选手:潘晓婷 准备好了
选手:刘翔 准备好了
选手:姚明 准备好了
姚明 起跑!
潘晓婷 起跑!
刘翔 起跑!

CountDownLatch与CyclicBarrier的区别


CountDownLatch是减计数方式,计数==0时释放所有等待的线程;CyclicBarrier是加计数方式,计数达到构造方法中参数指定的值时释放所有等待的线程。
CountDownLatch当计数到0时,计数无法被重置;CyclicBarrier计数达到指定值时,计数置为0重新开始(可以继续重用)。
CountDownLatch每次调用countDown()方法计数减一,调用await()方法只进行阻塞,对计数没任何影响;CyclicBarrier只有一个await()方法,调用await()方法计数加1,若加1后的值不等于构造方法的值,则线程阻塞。
CountDownLatch是让每一个线程都执行完成(run方法执行结束),在执行其他的任务
CyclicBarrier是每一个线程执行run方法中执行到一部分就阻塞了。等所有的线程都执行到这里之后就解除阻塞,继续执行run方法

CyclicBarrier重用:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class Test {
public static void main(String[] args) {
int N = 4;
CyclicBarrier barrier = new CyclicBarrier(N);

for(int i=0;i<N;i++) {
new Writer(barrier).start();
}

try {
Thread.sleep(25000);
} catch (InterruptedException e) {
e.printStackTrace();
}

System.out.println("CyclicBarrier重用");

for(int i=0;i<N;i++) {
new Writer(barrier).start();
}
}
static class Writer extends Thread{
private CyclicBarrier cyclicBarrier;
public Writer(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier;
}

@Override
public void run() {
System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据...");
try {
Thread.sleep(5000); //以睡眠来模拟写入数据操作
System.out.println("线程"+Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕");

cyclicBarrier.await(); //所有的线程在这里等待了。也就是每一个线程只执行了一部分。
} catch (InterruptedException e) {
e.printStackTrace();
}catch(BrokenBarrierException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"所有线程写入完毕,继续处理其他任务...");
}
}
}

执行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
线程Thread-0正在写入数据...
线程Thread-1正在写入数据...
线程Thread-3正在写入数据...
线程Thread-2正在写入数据...
线程Thread-1写入数据完毕,等待其他线程写入完毕
线程Thread-3写入数据完毕,等待其他线程写入完毕
线程Thread-2写入数据完毕,等待其他线程写入完毕
线程Thread-0写入数据完毕,等待其他线程写入完毕
Thread-0所有线程写入完毕,继续处理其他任务...
Thread-3所有线程写入完毕,继续处理其他任务...
Thread-1所有线程写入完毕,继续处理其他任务...
Thread-2所有线程写入完毕,继续处理其他任务...
CyclicBarrier重用
线程Thread-4正在写入数据...
线程Thread-5正在写入数据...
线程Thread-6正在写入数据...
线程Thread-7正在写入数据...
线程Thread-7写入数据完毕,等待其他线程写入完毕
线程Thread-5写入数据完毕,等待其他线程写入完毕
线程Thread-6写入数据完毕,等待其他线程写入完毕
线程Thread-4写入数据完毕,等待其他线程写入完毕
Thread-4所有线程写入完毕,继续处理其他任务...
Thread-5所有线程写入完毕,继续处理其他任务...
Thread-6所有线程写入完毕,继续处理其他任务...
Thread-7所有线程写入完毕,继续处理其他任务...

semaphore:信号量


Semaphore实现的功能就类似厕所有5个坑,假如有10个人要上厕所,那么同时只能有多少个人去上厕所呢?同时只能有5个人能够占用,当5个人中 的任何一个人让开后,
其中等待的另外5个人中又有一个人可以占用了。另外等待的5个人中可以是随机获得优先机会,也可以是按照先来后到的顺序获得机会,这取决于构造Semaphore对象时传入的参数选项。单个信号量的Semaphore对象可以实现互斥锁的功能,并且可以是由一个线程获得了“锁”,再由另一个线程释放“锁”,这可应用于死锁恢复的一些场合。
如果同时只允许一个线程来访问就可以实现Synchronized和lock的功能

例如:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class Test {

public static void main(String[] args) {
// 线程池
ExecutorService exec = Executors.newCachedThreadPool();
// 只能5个线程同时访问
final Semaphore semp = new Semaphore(5);
// 模拟20个客户端访问
for (int index = 0; index < 20; index++) {
final int NO = index;
Runnable run = new Runnable() {
public void run() {
try {
// 获取许可
semp.acquire();
System.out.println("Accessing: " + NO);
Thread.sleep((long) (Math.random() * 10000));
// 访问完后,释放
semp.release();
//availablePermits()指的是当前信号灯库中有多少个可以被使用
System.out.println("-----------------" + semp.availablePermits());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
exec.execute(run);
}
// 退出线程池
exec.shutdown();
}
}

执行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
Accessing: 0
Accessing: 2
Accessing: 1
Accessing: 4
Accessing: 3
-----------------1
Accessing: 8
-----------------1
Accessing: 11
-----------------1
Accessing: 7
-----------------1
Accessing: 9
-----------------1
Accessing: 12
-----------------1
Accessing: 5
-----------------1
Accessing: 10
-----------------1
Accessing: 6
-----------------1
Accessing: 13
-----------------1
Accessing: 14
Accessing: 15
-----------------0
-----------------1
Accessing: 16
-----------------1
Accessing: 17
-----------------1
Accessing: 18
-----------------1
Accessing: 19
-----------------1
-----------------2
-----------------3
-----------------4
-----------------5