JDK5新特性之线程同步工具类(三)

时间:2023-03-09 17:09:34
JDK5新特性之线程同步工具类(三)

一. Semaphore

Semaphore能够控制同一时候訪问资源的线程个数, 比如: 实现一个文件同意的并发訪问数.

Semaphore实现的功能就类似厕全部5个坑, 增加有十个人要上厕所, 那么同一时候仅仅能有5个人能够占用, 当5个人中的不论什么一个人离开后, 当中在等待的另外5个人中就有一个能够占用了. 另外等待的5个人中能够是随机获得优先机会,
也能够使依照先来后到的顺序获得机会, 这取决于构造Semaphore对象时传入的參数选项.

public class SemaphoreTest {
public static void main(String[] args) {
// 创建一个线程池
ExecutorService service = Executors.newCachedThreadPool();
final Semaphore sp = new Semaphore(3); // 表示当前有3盏灯(同意3个并发) // 启动5个线程
for (int i = 0; i < 5; i++) {
service.execute(new Runnable() {
public void run() {
try {
sp.acquire(); // 点亮一盏灯 // availablePermits: 表示能够使用的灯
System.out.println("线程" + Thread.currentThread().getName()
+ " 进入,当前已有" + (3 - sp.availablePermits()) + "个并发"); Thread.sleep((long) (Math.random() * 10000));
System.out.println("线程" + Thread.currentThread().getName() + " 即将离开"); sp.release(); // 熄灭一盏灯(释放) System.out.println("线程" + Thread.currentThread().getName()
+ " 已离开,当前已有" + (3 - sp.availablePermits()) + "个并发");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
}

单个Semaphore对象能够实现相互排斥锁的功能, 而且能够是由一个线程获得了"锁", 再由还有一个线程释放"锁", 这可应用于死锁恢复的一些场合.

线程pool-1-thread-1 进入,当前已有1个并发
线程pool-1-thread-2 进入,当前已有2个并发
线程pool-1-thread-4 进入,当前已有3个并发
线程pool-1-thread-4 即将离开
线程pool-1-thread-4 已离开。当前已有2个并发
线程pool-1-thread-3 进入。当前已有3个并发
线程pool-1-thread-2 即将离开
线程pool-1-thread-2 已离开,当前已有2个并发
线程pool-1-thread-5 进入。当前已有3个并发
线程pool-1-thread-5 即将离开
线程pool-1-thread-5 已离开,当前已有2个并发
线程pool-1-thread-1 即将离开
线程pool-1-thread-1 已离开,当前已有1个并发
线程pool-1-thread-3 即将离开
线程pool-1-thread-3 已离开,当前已有0个并发

二. CyclicBarrier

表示大家彼此等待。集合好后才開始出发,分散活动后又在指定地点集合碰面。

这就好比整个公司的人员里利用周末时间集体郊游一样。先各自从家出发到公司集合后,再同一时候出发到公园游玩,在指定地点集合后再同一时候開始就餐。

public class CyclicBarrierTest {

	public static void main(String[] args) {

		// 开启一个线程池
ExecutorService executorService = Executors.newCachedThreadPool(); // 參数3: 表示有3个到齐了才干够往下走。否则一直处于等待状态
final CyclicBarrier cb = new CyclicBarrier(3); // 创建3个线程
for(int i = 0; i < 3; i++){
executorService.execute(new Runnable(){
@Override
public void run(){
try {
Thread.sleep((long)(Math.random() * 1000)); // 每一个线程“歇息”的时间不同
System.out.println("线程" + Thread.currentThread().getName()
+ "即将到达集合地点1。当前已有" + (cb.getNumberWaiting() + 1)
+ "个已经到达," + (cb.getNumberWaiting() == 2 ? "都到齐了,继续前进" : "正在等候")); cb.await(); // 先到的等待后到的,当3个都到达时才会继续向下运行 Thread.sleep((long)(Math.random() * 1000)); // 每一个线程“歇息”的时间不同
System.out.println("线程" + Thread.currentThread().getName()
+ "即将到达集合地点2。当前已有" + (cb.getNumberWaiting() + 1)
+ "个已经到达," + (cb.getNumberWaiting() == 2 ? "都到齐了。继续前进" : "正在等候")); cb.await(); Thread.sleep((long)(Math.random()*1000)); // 每一个线程“歇息”的时间不同
System.out.println("线程" + Thread.currentThread().getName()
+ "即将到达集合地点3,当前已有" + (cb.getNumberWaiting() + 1)
+ "个已经到达。"+ (cb.getNumberWaiting() == 2 ? "都到齐了。继续前进" : "正在等候")); cb.await();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
executorService.shutdown();
}
}

三个线程干完各自的任务,在不同的时刻到达集合地点后,就能够接着忙各自的工作去了,再到达新的集合点,再去忙各自的工作。

线程pool-1-thread-2即将到达集合地点1,当前已有1个已经到达,正在等候
线程pool-1-thread-3即将到达集合地点1,当前已有2个已经到达,正在等候
线程pool-1-thread-1即将到达集合地点1,当前已有3个已经到达。都到齐了,继续前进
线程pool-1-thread-2即将到达集合地点2。当前已有1个已经到达。正在等候
线程pool-1-thread-3即将到达集合地点2,当前已有2个已经到达。正在等候
线程pool-1-thread-1即将到达集合地点2。当前已有3个已经到达。都到齐了。继续前进
线程pool-1-thread-3即将到达集合地点3,当前已有1个已经到达。正在等候
线程pool-1-thread-2即将到达集合地点3,当前已有2个已经到达,正在等候
线程pool-1-thread-1即将到达集合地点3,当前已有3个已经到达,都到齐了,继续前进

三. CountDownLatch

宛如倒计时计数器, 调用CountDownLatch对象的countDown方法就将计数器减一, 当计数到达0时, 则全部等待者或单个等待者開始运行.

应用: 裁判一声口令, 运动员同一时候開始奔跑, 当全部运动员都跑到终点后裁判发布结果. 还能够实现一个计划须要多个领导都签字后才干继续向下实施的情况.

public class CountDownLatchTest {  

    public static void main(String[] args) throws Exception {  

        ExecutorService service = Executors.newCachedThreadPool();  

        // 子计数器, count为1
final CountDownLatch subCounter = new CountDownLatch(1); // 主计数器, count为3
final CountDownLatch mainCounter = new CountDownLatch(3);
for(int i = 0; i < 3; i++){
service.execute(new Runnable() {
@Override
public void run() {
try {
System.out.println("线程 "+ Thread.currentThread().getName()+"正准备接受命令。"); subCounter.await(); // 子线程等待 System.out.println("线程 "+ Thread.currentThread().getName()+"已接受命令!");
Thread.sleep((long)Math.random() * 10000);
System.out.println("线程 "+ Thread.currentThread().getName()+"回应命令处理结果! "); mainCounter.countDown(); // 将计数器身上的计数减1, 当计数为0时, 主线程将開始运行 } catch (Exception e) {
e.printStackTrace();
}
}
} );
} Thread.sleep((long)Math.random() * 1000);
System.out.println("线程 "+ Thread.currentThread().getName()+"即将公布命令!"); subCounter.countDown(); // 将计数器身上的计数减1, 当计数为0时, 子线程開始运行
System.out.println("线程 "+ Thread.currentThread().getName()+"已发送命令,正在等待结果。"); mainCounter.await(); // 主线程等待
System.out.println("线程 "+ Thread.currentThread().getName()+"已收到全部响应结果! "); service.shutdown();
} }
线程 pool-1-thread-1正准备接受命令。
线程 pool-1-thread-3正准备接受命令!
线程 pool-1-thread-2正准备接受命令! 线程 main即将公布命令! 线程 main已发送命令。正在等待结果! 线程 pool-1-thread-2已接受命令!
线程 pool-1-thread-3已接受命令。
线程 pool-1-thread-1已接受命令。
线程 pool-1-thread-3回应命令处理结果!
线程 pool-1-thread-2回应命令处理结果!
线程 pool-1-thread-1回应命令处理结果。
线程 main已收到全部响应结果!

四. Exchanger

用于实现两个人之间的数据交换, 每一个人在完毕一定的事务后想与对方交换数据, 第一个先拿出数据的人将一直等待第二个人拿着数据到来时, 才干彼此交换数据:

public class ExchangerTest {

	public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final Exchanger exchanger = new Exchanger();
service.execute(new Runnable() {
@Override
public void run() {
try {
String data1 = "aaa";
System.out.println("线程 " + Thread.currentThread().getName() + " 正在把数据: " + data1 + " 换出去!"); Thread.sleep((long) Math.random() * 10000); String data2 = (String) exchanger.exchange(data1);
System.out.println("线程 " + Thread.currentThread().getName() + " 换回的数据为:" + data2);
} catch (Exception e) { }
}
}); service.execute(new Runnable() {
@Override
public void run() {
try {
String data1 = "bbb";
System.out.println("线程 " + Thread.currentThread().getName() + " 正在把数据: " + data1 + " 换出去!"); Thread.sleep((long) Math.random() * 10000); String data2 = (String) exchanger.exchange(data1);
System.out.println("线程 " + Thread.currentThread().getName() + " 换回的数据为:" + data2);
} catch (Exception e) { }
}
}); } }
线程 pool-1-thread-1 正在把数据: aaa 换出去!

线程 pool-1-thread-2 正在把数据: bbb 换出去!
线程 pool-1-thread-1 换回的数据为:bbb
线程 pool-1-thread-2 换回的数据为:aaa