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




评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值