线程工具类
1.Semaphore实现信号灯,可以控制访问线程的个数。信号量初始化10,最多只有10个线程并发访问
2.CyclicBarrier:提供一种阻塞机制,等待所有线程到达某一点集合之后,再继续向下运行
3.CountDownLatch:通过计数器技术,实现一同多,或多通知一
4.Exchanger:提供一种阻塞机制,实现俩个线程交换数据
1.Semaphore实现信号灯
一.概述:
Semaphore可以维护当前访问自身的线程个数,并提供了同步机制。使用Semaphore可以控制同时访问资源的线程个数,例如,实现一个文件允许的并发问题。
(1)Semaphore实现的功能就类似厕所有5个坑,假如有十个人要上厕所,那么同时能有多少个人上厕所呢?同时只能有5个人能够占用,当5个人中的任何一个人让开后,其中在等待的另外5个人中又有一个可以占用了。
(2)另外等待的5个人中可以是随机获得优先机会,也可以是按照先来后到的顺序获得机会,这取决于构造Semaphore对象时传入的参数选项(构造函数参数配置)。
二.单个信号量的Semaphore对象可以实现互斥锁的功能,并且可以是由一个线程获得了”锁”,再由另一个线程释放锁,这可应用于死锁恢复的一些场合。
(解释:通过信号量来实现线程之间的互斥,其特点是可以由多个线程对信号量对象进行控制。锁的特点是当一个线程产生死锁,锁释放不了,别的线程肯定进不来。使用信号量,如果线程1死锁了,可以在另一个线程2中释放信号量,别的线程又可以进入)
代码示例:
public class SemaphoreTest {
public static void main(String[] args) {
ExecutorService execut= Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(3);
for(int i=0;i<10;i++){
Runnable runnable = new Runnable(){
public void run() {
try {
//获取信号量的许可
semaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程" + Thread.currentThread().getName() + "进入,当前已有" + (3-semaphore.availablePermits()) + "个并发");
try {
Thread.sleep((long)(4000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程" + Thread.currentThread().getName() + "即将离开");
//释放信号量
semaphore.release();
System.out.println("线程" + Thread.currentThread().getName() +
"已离开,当前已有" + (3-semaphore.availablePermits()) + "个并发");
}
};
execut.execute(runnable);
}
}
}
2、CyclicBarrier:
在创建CyclicBarrier对象的时候,还可以传入一个Runnable当障碍点到齐后执行。
表示大家彼此等待,大家集合好后才开始出发,分散活动后又在指定地点集合碰面,这就好比整个公司的人员利用周末时间集体郊游一样,先各自从家出发到公司集合后,再同时出发到公园游玩,在指定地点集合后再同时开始就餐。。。
代码示例:
public class CyclicBarrierTest {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final CyclicBarrier cb = new CyclicBarrier(3,new Runnable() {
//当所有线程到达barrier时执行
@Override
public void run() {
System.out.println("Inside Barrier");
}
});
for(int i=0;i<4;i++){
Runnable runnable = new Runnable(){
public void run(){
try {
Thread.sleep((long)(Math.random()*10000));
System.out.println("线程" + Thread.currentThread().getName() + "即将到达集合地点1,当前已有" + (cb.getNumberWaiting()+1) + "个已经到达," + (cb.getNumberWaiting()==2?"都到齐了,继续走啊":"正在等候"));
cb.await();
Thread.sleep((long)(Math.random()*10000));
System.out.println("线程" + Thread.currentThread().getName() + "即将到达集合地点2,当前已有" + (cb.getNumberWaiting()+1) + "个已经到达," + (cb.getNumberWaiting()==2?"都到齐了,继续走啊":"正在等候"));
cb.await();
Thread.sleep((long)(Math.random()*10000));
System.out.println("线程" + Thread.currentThread().getName() + "即将到达集合地点3,当前已有" + (cb.getNumberWaiting() + 1) + "个已经到达," + (cb.getNumberWaiting()==2?"都到齐了,继续走啊":"正在等候"));
cb.await();
} catch (Exception e) {
e.printStackTrace();
}
}
};
service.execute(runnable);
}
service.shutdown();
}
}
3.CountDownLatch:
1.犹如倒计时计数器,调用CountDownLatch对象的countDown方法就将计数器减1,当计数到达0时。则所有等待者或单个等待者开始执行。这直接通过代码来说明CountDownLatch的作用,这样学员的理解效果更加直接。
2.可以实现一个人(也可以是多个人)等待其他所有人都来通知他,可以实现一个人通知多个人的效果,类似裁判一声口令,运动员同时开始奔跑,或者所有运动员都跑到终点后裁判才可以公布结果,用这个功能做百米赛跑的游戏程序不错哦
还可以实现一个计划需要多个领导都签字后才能继续向下实施的情况。
代码示例:
public class CountDownLatchTest {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final CountDownLatch cdOrder = new CountDownLatch(1);
final CountDownLatch cdAnswer = new CountDownLatch(3);
for(int i=0;i<3;i++){
Runnable runnable = new Runnable(){
public void run() {
try {
System.out.println("线程"+Thread.currentThread().getName()+"正准备接收任务");
//cdOrder计数器不为0时,等待;当cdOrder计数器为0时,向下运行
cdOrder.await();
System.out.println("线程"+Thread.currentThread().getName()+"已接收任务");
Thread.sleep((long)(Math.random()*10000));
System.out.println("线程" + Thread.currentThread().getName() + "回应命令处理结果");
//cdAnswer计数器减1,cdAnswer计数器为0,通知main线程运行
cdAnswer.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
service.execute(runnable);
}
try {
Thread.sleep((long)(Math.random()*10000));
System.out.println("线程"+Thread.currentThread().getName()+"即将发布任务");
//cdOrder计数器减1,cdOrder计数器为0,通知其他线程运行
cdOrder.countDown();
System.out.println("线程" + Thread.currentThread().getName() + "已发送命令,正在等待结果");
//等待cdAnswer计数器减为0
cdAnswer.await();
System.out.println("线程" + Thread.currentThread().getName() + "已收到所有响应结果");
} catch (InterruptedException e) {
e.printStackTrace();
}
service.shutdown();
}
}
4.Exchanger
用于实现两个人之间的数据交换,每个人在完成一定的事务后想与对方交换数据,第一个先拿出数据的人将一直等待第二个人拿着数据到来时,才能彼此交换数据。
代码示例:
public class ExchangerTest {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final Exchanger exchanger = new Exchanger();
service.execute(new Runnable(){
public void run() {
try {
String data1 = "zxx";
System.out.println("线程" + Thread.currentThread().getName() + "正在把数据" + data1 +"换出去");
Thread.sleep((long)(Math.random()*10000));
//通过Exchanger对象,等待换取俩线程间数据,等待另一个线程到来
String data2 = (String)exchanger.exchange(data1);
System.out.println("线程" + Thread.currentThread().getName() + "换回的数据为" + data2);
}catch(Exception e){
}
}
});
service.execute(new Runnable(){
public void run() {
try {
String data1 = "lhm";
System.out.println("线程" + Thread.currentThread().getName() + "正在把数据" + data1 +"换出去");
Thread.sleep((long)(Math.random()*10000));
//通过Exchanger对象,等待换取俩线程间数据,等待另一个线程到来
String data2 = (String)exchanger.exchange(data1);
System.out.println("线程" + Thread.currentThread().getName() + "换回的数据为" + data2);
}catch(Exception e){
}
}
});
}
}