Java线程(五):JUC包线程同步工具

时间:2023-02-13 13:12:51

一、线程同步工具 Semaphore

1、Semaphore 可以维护当前访问自身的线程个数,并提供了同步机制。使用 Semaphore 可以控制同时访问资源的线程个数。相当于蹲坑,一共有3个坑,10个人来,只能三个三个上,三个并发。例如,实现一个文件允许的并发访问数。

2、单个Semaphore,可以实现线程互斥锁的功能。并且可以由一个线程得到‘锁’,再由另一个线程释放‘锁’。这可应用于死锁回复的一些场合。如一个人上厕所,晕在里面了,本来只有里面那个人才能释放。现在可以由其他人释放。

3、Semaphore的具体使用:信号灯。

1)创建:permits-灯(Semaphore)的个数;fair,实现线程进入优先级。true的时候,先到先进

final  Semaphore sp = new Semaphore(int permits, boolean fair) ;

2) Semaphore的获取与释放:

Semaphore.acquire();//获取信号灯
Semaphore.release();//释放获取的灯
Semaphore.availablePermits() //被拿走的灯的个数


4、实例:

package com.newThread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
/**
 * @title 线程同步工具1:Semaphore实现信号灯
 * @description 相当于蹲坑,一共有3个坑,10个人来,只能三个三个上,三个并发。
 * 		1-Semaphore 可以维护当前访问自身的线程个数,并提供了同步机制。使用Semaphore可以控制同时访问
 * 			资源的线程个数。例如,实现一个文件允许的并发访问数。
 * 		2-单个Semaphore,可以实现线程互斥锁的功能。并且可以由一个线程得到‘锁’,再由另一个线程释放‘锁’
 * 			这可应用于死锁回复的一些场合。如一个人上厕所,晕在里面了,本来只有里面那个人才能释放。现在可以由其他人释放。
 * @author SAM-SHO 
 * @Date 2014-8-30
 */
public class SemaphoreTest {
	public static void main(String[] args) {
		//创建线程池,newCachedThreadPool,有多少任务,开多少线程。但是这边会考虑灯的情况
		ExecutorService service = Executors.newCachedThreadPool();
	
		//建立信号灯,有参数fair,实现线程进入优先级。true的时候,先到先进
		final  Semaphore sp = new Semaphore(3);//只有三个灯
		
		//创建10个线程
		for(int i=0;i<10;i++){
			Runnable runnable = new Runnable(){
					public void run(){
					try {
						sp.acquire();//获取信号灯
					} catch (InterruptedException e1) {
						e1.printStackTrace();
					}
					
					//sp.availablePermits() 被拿走的灯的个数
					System.out.println("线程" + Thread.currentThread().getName() + 
							"进入,当前已有" + (3-sp.availablePermits()) + "线程");
					try {
						Thread.sleep((long)(Math.random()*10000));
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println("线程" + Thread.currentThread().getName() + 
							"即将离开");					
					sp.release();//释放获取的灯
					//下面代码有时候执行不准确
					System.out.println("线程" + Thread.currentThread().getName() + 
							"已离开,当前已有" + (3-sp.availablePermits()) + "线程");					
				}
			};
			service.execute(runnable);//执行10个线程			
		}
	}

}


二、线程同步工具 CyclicBarrier

1、CyclicBarrier :实现循环使用的路障。

2、表示大家彼此等待,大家集合好后才开始出发,分散活动后又在指定地点集合碰面。如好比整个公司的人员利用周末集体郊游,先各自从家出发到公司集合后,再同时出发到公园游玩。先到的阻塞,不行动。在指定地点集合后再同时开始就餐...

3、CyclicBarrier  的使用

CyclicBarrier.getNumberWaiting() //正在等待的线程个数,三个就能走,从 0 开始数数
CyclicBarrier.await();//只有三个都到了才会运行下去,即集合地点

4、实例代码:

package com.newThread;


import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 
 * CyclicBarrierTest.java
 *
 * @title 线程同步工具2:路障,实现循环使用的路障
 * @description CyclicBarrier :表示大家彼此等待,大家集合好后才开始出发,分散活动后又在指定地点集合碰面
 * 		1-好比整个公司的人员利用周末集体郊游,先各自从家出发到公司集合后,再同时出发到公园游玩。先到的阻塞,不行动。
 * 			在指定地点集合后再同时开始就餐...
 * @author SAM-SHO 
 * @Date 2014-8-31
 */
public class CyclicBarrierTest {

	public static void main(String[] args) {
		//创建线程池
		ExecutorService service = Executors.newCachedThreadPool();
		
		//创建三个路障,即一定要三个一起行动
		final  CyclicBarrier cb = new CyclicBarrier(3);
		
		//3个线程
		for(int i=0;i<3;i++){
			Runnable runnable = new Runnable(){
					public void run(){
					try {
						Thread.sleep((long)(Math.random()*10000));	
						
						//cb.getNumberWaiting() 正在等待的线程个数,三个就能走,
						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();//关闭
	}
}


三、线程同步工具 CountDownLatch  

1、CountdownLatch 倒计时计时器。

2、犹如倒计时的计时器,调用CountdownLatch对象的countDown()方法,就将计时器减一当计时器到达0时,则所有线程或单个线程开始执行。

3、可以实现一个人(多人)等待其他所有人都来通知他,可以实现一个通知多个人的效果。类似裁判一声令下运动员同时开始奔跑。

3、实例:

package com.newThread;


import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 
 * CountdownLatchTest.java
 *
 * @title 线程同步工具3:CountdownLatch 倒计时
 * @description 
 * 		1-犹如倒计时的计时器,调用CountdownLatch对象的countDown()方法,就将计时器减一
 * 			当计时器到达0时,则所有线程或单个线程开始执行
 * 		2-可以实现一个人(多人)等待其他所有人都来通知他,可以实现一个通知多个人的效果。类似裁判一声令下
 * 			运动员同时开始奔跑
 * @author SAM-SHO 
 * @Date 2014-8-31
 */
public class CountdownLatchTest {

	public static void main(String[] args) {
		//创建线程池
		ExecutorService service = Executors.newCachedThreadPool();
		
		//创建倒计时的计时器,初始值分别为1和3。他们的计时器在别人手中。
		//可以一个人有三个人的计时器,也可以三个人有一个人的计时器
		//即实现双方的数据交互
		final CountDownLatch cdOrder = new CountDownLatch(1);//子线程的计时器
		final CountDownLatch cdAnswer = new CountDownLatch(3);//主线程的计时器		
		
		//创建3子个线程
		for(int i=0;i<3;i++){
			Runnable runnable = new Runnable(){//她们的计时器为cdAnswer
					public void run(){
					try {
						System.out.println("线程" + Thread.currentThread().getName() + 
								"正准备接受命令");						
						cdOrder.await();//子线程自己等待
						System.out.println("线程" + Thread.currentThread().getName() + 
						"已接受命令");								
						Thread.sleep((long)(Math.random()*10000));	
						System.out.println("线程" + Thread.currentThread().getName() + 
								"回应命令处理结果");						
						cdAnswer.countDown();//主线程,计数器减一。只有三个子线程都结束,才会结束主线程的计时器						
					} catch (Exception e) {
						e.printStackTrace();
					}				
				}
			};
			service.execute(runnable);
		}		
		try {
			
			//主线程:main 她的计时器为cdAnswer
			Thread.sleep((long)(Math.random()*10000));
		
			System.out.println("线程" + Thread.currentThread().getName() + 
					"即将发布命令");						
			cdOrder.countDown();//让三个子线程,计时器减一,发布命令
			System.out.println("线程" + Thread.currentThread().getName() + 
			"已发送命令,正在等待结果");	
			cdAnswer.await();//主线程等待
			System.out.println("线程" + Thread.currentThread().getName() + 
			"已收到所有响应结果");	
		} catch (Exception e) {
			e.printStackTrace();
		}				
		service.shutdown();

	}
}


四、线程同步工具 Exchanger

1、Exchanger:用于实现两个人之间的数据交换。

2、每个人在完成一定的事务后想与对方交换数据,第一个先拿出数据的人将一直等待第二个人拿着数据到来时,才能彼此交换数据。

3、实例:

package com.newThread;


import java.util.concurrent.Exchanger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 
 * ExchangerTest.java
 *
 * @title 线程同步工具4:Exchanger 
 * @description  
 * 		1-用于实现两个人之间的数据交换。每个人在完成一定的事务后想与对方交换数据,
 * 			第一个先拿出数据的人将一直等待第二个人拿着数据到来时,才能彼此交换数据
 * @author SAM-SHO 
 * @Date 2014-8-31
 */
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 = "   邵小宝    ";
					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(){
			public void run() {
				try {				

					String data1 = "   赵小妞   ";
					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){
					
				}				
			}	
		});		
	}
}