1.CountDownLatch(门闩) 用法
1.应用场景:实现类似计数器的功能,让主线程等待所有子线程完成再执行。
案例:
比如有一个任务A,它要等待其他4个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能了。
2.CountDownLatch类只提供了1个构造器:
CountDownLatch(int count);//参数count为计数值
3.CountDownLatch类中重要的3个方法:
await()
:调用await()方法的线程会被挂起,它会等待直到count为0时才继续执行。
await(long timeout,TimeUnit unit)
:和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行。
countDown()
: 将count值减1。
4.示例代码:主线程等待2个子线程执行完毕,再执行主线程
final CountDownLatch latch = new CountDownLatch(2);
// 开启2个线程
for (int i = 1; i <= 2; i++) {
new Thread("线程"+i){
public void run(){
try{
System.out.println(Thread.currentThread().getName()+"正在执行");
Thread.sleep(2000);
}catch(Exception e){
e.printStackTrace();
}finally {
System.out.println(Thread.currentThread().getName()+"执行完毕");
latch.countDown();
}
}
}.start();
}
try{
System.out.println("主线程等待2个子线程执行完毕...");
latch.await();
System.out.println("2个子线程已经执行完毕");
System.out.println("继续执行主线程");
}catch(Exception e){
e.printStackTrace();
}
输出信息:
主线程等待2个子线程执行完毕…
线程1正在执行
线程2正在执行
线程2执行完毕
线程1执行完毕
2个子线程已经执行完毕
继续执行主线程
2.CyclicBarrier(循环栅栏) 用法
1.应用场景:可以实现让一组线程等待至某个状态之后再全部同时执行,CyclicBarrier可以被重用
案例:
主要是让线程协同工作:比如有3个线程,每个线程都有3个任务,其中要所有线程完成上个任务才能执行下个任务。
2.CyclicBarrier类提供2个构造器:
CyclicBarrier(int parties, Runnable barrierAction)
CyclicBarrier(int parties)
参数parties :指让多少个线程或者任务等待至barrier状态。
参数barrierAction :为当这些线程都达到barrier状态时会执行的内容。
3.CyclicBarrier类中最重要的方法就是await方法,它有2个重载版本
await()
:用来挂起当前线程,直至所有线程都到达brrier状态再同时执行后续任务;
await(long timeout,TimeUnit unit)
:线程等待至一定的时间,如果还有线程没有到达barrier状态就直接让到达barrier的线程执行后续任务;
4.示例代码:比如有3个线程,每个线程都有3个任务,其中要所有线程完成上个任务才能执行下个任务。
final CyclicBarrier barrier = new CyclicBarrier(3);
for (int i = 1; i <= 3; i++) {
new Thread("线程"+i) {
public void run() {
try {
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + ",完成任务1");
barrier.await();// 等待所有线程都await
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + ",完成任务2");
barrier.await();// 重用
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + ",完成任务3");
barrier.await();// 重用
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
输出信息:
线程1,完成任务1
线程2,完成任务1
线程3,完成任务1
线程2,完成任务2
线程3,完成任务2
线程1,完成任务2
线程3,完成任务3
线程1,完成任务3
线程2,完成任务3
3.Semaphore(信号量) 用法
1.应用场景:
Semaphore可以控制同时访问的线程个数,通过acquire()获取1个许可,如果没有就等待,而release()释放1个许可。
2.Semaphore类提供了2个构造器:
Semaphore(int permits)
Semaphore(int permits,boolean fair)
参数permits :表示许可数目,即同时可以允许多少线程进行访问
参数fair :表示是否是公平的,即等待时间越久的越先获取许可
3.Semaphore类中重要的3个方法:
acquire()
:用来获取一个许可,若无许可能够获得,则会一直等待,直到获得许可。
release()
: 用来释放许可。注意,在释放许可之前,必须先获得许可。
tryAcquire()
: 尝试获取一个许可,若获取成功,则立即返回true,若获取失败,则立即返回false
4.示例代码:比如工厂有5台机器,但是有8个工人,1台机器同时只能被1个工人使用,只有使用完了,其他工人才能继续使用。
// 5台机器
final Semaphore semaphore = new Semaphore(5);
// 8个工人
for (int i = 1; i <= 8; i++) {
new Thread("工人" + i) {
public void run() {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + ",占用1个机器");
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() + ",释放了机器");
semaphore.release();
}
}
}.start();
}
输出信息:
工人1,占用1个机器
工人4,占用1个机器
工人5,占用1个机器
工人3,占用1个机器
工人2,占用1个机器
工人1,释放了机器
工人3,释放了机器
工人4,释放了机器
工人6,占用1个机器
工人8,占用1个机器
工人5,释放了机器
工人2,释放了机器
工人7,占用1个机器
工人8,释放了机器
工人6,释放了机器
工人7,释放了机器
5.示例代码:接口限流案例,5个许可证,10个线程去抢许可,抢到许可证就执行,未抢到就不执行。
// 5个许可证
final Semaphore semaphore = new Semaphore(5);
// 10个线程
for (int i = 1; i <= 10; i++) {
new Thread("线程" + i) {
public void run() {
boolean flag = semaphore.tryAcquire();
if(flag){
try {
System.out.println(Thread.currentThread().getName() + ",抢到1个许可");
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() + ",释放了许可");
semaphore.release();
}
}else{
System.out.println(Thread.currentThread().getName() + ",没抢到许可");
}
}
}.start();
}
输出信息:
线程1,抢到1个许可
线程3,抢到1个许可
线程5,抢到1个许可
线程2,抢到1个许可
线程4,抢到1个许可
线程7,没抢到许可
线程6,没抢到许可
线程9,没抢到许可
线程8,没抢到许可
线程10,没抢到许可
线程1,释放了许可
线程3,释放了许可
线程2,释放了许可
线程4,释放了许可
线程5,释放了许可