Latch,Semaphore,Barrier是java中的三大同步工具类。首先了解下他们的特性和应用场景:{ps:有些东西纯属个人理解,有不正确的地方请指教}
Latch(门闩):闭锁是一个同步工具类,可以延迟线程执行直到所有的线程都到达一个状态。
作用:
1、确保某个计算在其所需要的所有资源都初始化后再执行;
2、确保某个服务在其他所依赖的服务都执行完毕后再执行;
3、等个某个操作的所有参与者都准备就绪后再继续执行。
信号量:信号量用来控制同时访问资源和执行当前动作的线程个数
信号量应用场景:
1、实现资源池,如数据库连接池;
2、将任意collection 转换成有界的阻塞collection
3、单个信号量的Semaphore对象可以实现互斥锁,而且可以由一个线程锁定,另外一个线程释放,是解决死锁的一种方法
Barrier(障碍):Barrier等待一组活动都完成或者一组活动都开始后再执行线程的后续操作,如组织去公园,等人到齐了才出发
Barrier跟Latch区别:
1、Barrier的定义是所有线程都得到达同一点才能继续向下执行,Latch的定义是当Latch达到一个 临界(终点)状态时所有的线程才能获得往下执行的通道。
2、Latch是线程等待某个事件完成,Barrier是线程等待其他线程
3、个人理解觉得Latch是被动等待,Barrier是主动等待。Barrier等待方会被等待方是同一属性级别,都是线程。如自发组织旅游,所有人按约定 的时间,地点在同一个地方集合,先到的人等待其他人都到齐后再一起出发。Latch就像开会签到,开会前参与者先进行签到,当组织者等到所有参与者都签到
后再进入会议主题
下面是三种同步工具类的Demo以及测试代码:
Latch:
package cn.com.chp5.latch;
import java.util.concurrent.CountDownLatch;
/**
* <th>Latch例子<th>
*
* <p>
* 功能:通过CountDownLatch begin事件触发线程等待,当所有子线程全部准备就绪后再开始执行线程,当所有子线程执行完毕后的end触发主线程
* 整个功能完成记录所有子线程从就绪到执行完的执行时间记录
* <p>
*
* <p>
* Latch(门闩):闭锁是一个同步工具类,可以延迟线程执行直到所有的线程都到达一个状态。
* 作用:1、确保某个计算在其所需要的所有资源都初始化后再执行;
* 2、确保某个服务在其他所依赖的服务都执行完毕后再执行;
* 3、等个某个操作的所有参与者都准备就绪后再继续执行。
* <p>
*
* @author tianxingjian <h1>date: 2014-01-16</h1>
*/
public class LatchDemo {
public Long runTask(int threadNum, final Runnable runnable) throws InterruptedException{
final CountDownLatch begin = new CountDownLatch(1);
final CountDownLatch end = new CountDownLatch(threadNum);
for(int i = 0; i < threadNum; i++){
Thread th = new Thread(){
public void run(){
try{
begin.await();
try {
runnable.run();
}finally{
end.countDown();
}
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
};
th.start();
}
Long beginSec = System.nanoTime();
//begin 和end顺序千万不能写错,不然程序就死锁了。
begin.countDown();
end.await();
Long endSec = System.nanoTime();
return endSec - beginSec;
}
}
package cn.com.chp5.latch;
import java.util.Random;
public class LatchTest {
private static final int count = 10;
public static void main(String[] args) throws InterruptedException {
System.out.println("进入主线程:。。。。。。");
Long l = new LatchDemo().runTask(count, new TaskAction());
System.out.println("工作线程执行时间:" + l);
}
}
class TaskAction implements Runnable{
@Override
public void run() {
Random rand = new Random(1000);
int count = rand.nextInt() + 1;
for(int i = 0; i < count; i++){
int result = i*i;
}
}
}
Semaphore:
package cn.com.chp5.semaphore;
import java.util.concurrent.Semaphore;
/**
* <th>Semaphore例子<th>
*
* <p>
* 预期结果:线程可以启动多个,但是同时执行aquireAction的线程最多count个
* <p>
*
* <p>
* 信号量作用:信号量用来控制同时访问资源和执行当前动作的线程个数
* 信号量应用场景:1、实现资源池,如数据库连接池;
* 2、将任意collection 转换成有界的阻塞collection
* 3、单个信号量的Semaphore对象可以实现互斥锁,而且可以由一个线程锁定,另外一个线程释放,是解决死锁的一种方法
* <p>
*
* @author tianxingjian <h1>date: 2014-01-16</h1>
*/
public class SemaphoreDemo {
private final Semaphore se;
private int count;
public SemaphoreDemo(int count){
this.count = count;
se = new Semaphore(count);
}
public void doAction(){
System.out.println(Thread.currentThread() + "[准备申请信号量!]");
aquireAction();
System.out.println(Thread.currentThread() + "[准备释放信号量!]");
releaseAction();
}
public void aquireAction(){
try {
//acquire:获得;下面方法是获取信号量
se.acquire();
System.out.println(Thread.currentThread() + "[信号量申请成功,当前aquireAction()并发数为:" + (count - se.availablePermits()) + "]");
Thread.sleep((long)Math.random()*10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void releaseAction(){
se.release();
System.out.println(Thread.currentThread() + "[信号量释放成功!]");
}
}
package cn.com.chp5.semaphore;
public class SemaphoreTest {
public static void main(String[] args) {
final SemaphoreDemo seDemo = new SemaphoreDemo(3);
int threadCount = 10;
String []threadName = new String[]{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K"};
for(int i = 0; i < threadCount; i++){
Thread th = new Thread(new Runnable() {
@Override
public void run() {
seDemo.doAction();
}
}, threadName[i]);
th.start();
}
}
}
Barrier:
package cn.com.chp5.barrier;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* <th>Barrier例子<th>
*
* <p>
* Barrier(障碍):Barrier等待一组活动都完成或者一组活动都开始后再执行线程的后续操作,如组织去公园,等人到齐了才出发
* Barrier跟Latch区别: 1、Barrier的定义是所有线程都得到达同一点才能继续向下执行,Latch的定义是当Latch达到一个
* 临界(终点)状态时所有的线程才能获得往下执行的通道。
* 2、Latch是线程等待某个事件完成,Barrier是线程等待其他线程
* 3、个人理解觉得Latch是被动等待,Barrier是主动等待。Barrier等待方会被等待方是同一属性级别,都是线程。如自发组织旅游,所有人按约定
* 的时间,地点在同一个地方集合,先到的人等待其他人都到齐后再一起出发。Latch就像开会签到,开会前参与者先进行签到,当组织者等到所有参与者都签到
* 后再进入会议主题
* <p>
*
* @author tianxingjian <h1>date: 2014-01-16</h1>
*/
public class BarrierDemo {
private final CyclicBarrier barrier;
public BarrierDemo(int barCount, Runnable runnable){
barrier = new CyclicBarrier(barCount, runnable);
}
public void doAction(String [] thNames){
for(int i = 0; i < thNames.length; i++){
Thread th = new Thread(new BTRunnable(), thNames[i]);
System.out.println("线程【" + thNames[i] + "】初始化完毕!");
th.start();
}
}
class BTRunnable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread() + "[阻塞前操作!]" + System.currentTimeMillis());
try {
barrier.await();
Thread.sleep((long) (Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + "[阻塞后操作!]" + System.currentTimeMillis());
}
}
}
package cn.com.chp5.barrier;
/**
* <th>Barrier例子<th>
*
* <p>
结果:改变BarrierDemo barrier = new BarrierDemo(5, new Runnable())中的参数5会发现“执行次数:”打印次数不一样,这是
因为每有一组线程数量达到这个数时候就会往下执行,然后Barrier reset值,其他线程进入后重新组成一组线程通过这个Barrier。
如果5改变为非thNames长度的因子,程序会阻塞,因为有最后一组线程数达不到这个值导致这些线程一直在等待状态
* <p>
*
* @author tianxingjian <h1>date: 2014-01-16</h1>
*/
public class BarrierTest {
private static String []thNames = new String[]{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J"};
public static void main(String[] args) {
BarrierDemo barrier = new BarrierDemo(5, new Runnable() {
int count = 0;
@Override
public void run() {
System.out.println("执行次数: " + ++count);
}
});
barrier.doAction(thNames);
}
}