多线程工具类CountDownLatch,CyclicBarrier,Semaphore

多线程工具类CountDownLatch,CyclicBarrier,Semaphore

CountDownLatch和CyclicBarrier功能很像,

先说他们的区别吧,在我看来就两点,

1、CountDownLatch类似于计数器的减法,CyclicBarrier类似于计数器的加法

2、CountDownLatch中的计数器从构造注入的初始值减到0时,不重置,CyclicBarrier的计数器也有以一个初始值,不过CyclicBarrier是加法,当加到那个初始值的时候,重置为0。

一、CountDownLatch

 * java.util.concurrent包下的多线程工具类
 * CountDownLatch 类似于一个计数器,是一个同步工具类,协调多个线程之间的同步
 * 特点: 做减法计算,当计数为0的时,唤醒阻塞线程
 * 使用场景:执行主线程之前需要执行一些特定的线程
 * CountDownLatch的缺点:CountDownLatch中的计数器从构造注入的初始值减到0时,不重置,不想CyclicBarrier可以重复使用。

举例:执行主线程之前,需要先执行50个子线程

public class MyCountDownLatch {
    public static final int COUNTDOWNLATCHCOUNT = 50;
    public static int i = 0;
    static Lock lock = new ReentrantLock();
    public static void inadd(){
        lock.lock();
        try{
            i++;
        }finally {
            lock.unlock();
        }
    }
	public static void main(String[] args) {
		
			long startTime = System.currentTimeMillis();
			try {
				ExecutorService executorService = Executors.newFixedThreadPool(20);
				CountDownLatch countDownLatch = new CountDownLatch(COUNTDOWNLATCHCOUNT);
				for (int i = 0; i < COUNTDOWNLATCHCOUNT; i++) {
					//初始化计数器的值
					executorService.execute(new MyCountDownLatchThread(countDownLatch,i));
				}
				countDownLatch.await();
				executorService.shutdown();
				long endTime = System.currentTimeMillis();
				System.out.println("---执行主线程---,执行其他线程耗时"+(double)(endTime-startTime)/1000+"秒");
				System.out.println("--执行线程数:"+i);

			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
				
	}
	private static class MyCountDownLatchThread implements Runnable{
        private CountDownLatch latch;
        private int flag;
		
		public MyCountDownLatchThread(CountDownLatch latch, int flag) {
			super();
			this.latch = latch;
			this.flag = flag;
		}

		@Override
		public void run() {
			// TODO Auto-generated method stub
			synchronized (this) {
				try {
					Random random = new Random();
					int randomNum = random.nextInt((3000 - 1000)+1)+1000;//制造1000到3000之间的随机数
					Thread.sleep(randomNum);
					System.out.println("线程"+flag+"已执行完毕,耗时"+(double)randomNum/1000+"秒");
					inadd();
					latch.countDown(); 
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		
	}

}

执行效果:

二、CyclicBarrier

 * java.util.concurrent包下的多线程工具类
 * CyclicBarrier大体的意思是可循环使用的屏障
 * 特点:加计数方式,计数达到指定值时释放所有的等待线程,并计数重置为0重新开始
 * 含义:用构造方法设置阻塞屏障数为n,调用await()方法计数加1,若加1后的值不等于构造器方法
 * 设置的n值,则线程阻塞,达到n值时。释放所有的等待线程,计数值重新为0,以此类推。
 * 优点:可重复使用

例子:人员面试,有三个环节(初面,二面,三面),总共有4个人员来面试,要求公司规定初面结束才能二面,三面,实现:

public class MyCyclicBarrier {
	public static final int MS_COUNT = 3; //面试环节数量
	//关键点:阻塞数等于面试人员数量,以每个人员为一个线程,每次面试,只有所有人都面过的时候,
	//阻塞放开,执行下次面试。
	public static final int PERSIONCOUNT = 4; //面试人员的数量
	public static final int ZS_COUNT = 4; //阻塞数
	
   	public static void main(String[] args) {		
		try {
			
			ExecutorService executorService = Executors.newFixedThreadPool(10);
			CyclicBarrier cyclicBarrier = new CyclicBarrier(ZS_COUNT);
		    for (int i = 0; i < PERSIONCOUNT; i++) {
		    	executorService.execute(new MyCyclicBarrierThread(cyclicBarrier,i));
			}
		    executorService.shutdown();
		}catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		

	}
	
	private static class MyCyclicBarrierThread implements Runnable{
        private CyclicBarrier cer;
        private int persionName;
        
		public MyCyclicBarrierThread(CyclicBarrier cer, int persionName) {
			super();
			this.cer = cer;
			this.persionName = persionName;
		}

		@Override
		public void run() {
			// TODO Auto-generated method stub
			synchronized (this) {
				for (int i = 0; i < MS_COUNT; i++) {
					Random random = new Random();
					int randomNum = random.nextInt((3000 - 1000)+1)+1000;//制造1000到3000之间的随机数
					try {
						Thread.sleep(randomNum);
						switch (i) {
						case 0:
							System.out.println("人员"+persionName+"已通过初面,耗时"+(double)randomNum/1000+"天");
							break;
						case 1:
							System.out.println("人员"+persionName+"已通过二面,耗时"+(double)randomNum/1000+"天");
							break;
						case 2:
							System.out.println("人员"+persionName+"已通过三面,耗时"+(double)randomNum/1000+"天");
							break;							
						default:
							break;
						}
						this.cer.await();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (BrokenBarrierException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}
		
	}
}

实现效果:

这里有个要强调的地方:

阻塞数等于面试人员数量,目的是以每个人员为一个线程,每次面试,只有所有人都面过的时候,
阻塞放开,执行下次面试。

否则:当阻塞数小于面试人员数量时,效果:

实现结果会出现问题。

当阻塞数大于面试人员数量时,效果:

原因是在for循环中,await()添加计数没有达到CyclicBarrier的屏障时,线程不会放开,会一直阻塞

三、Semaphore

 * Semaphore翻译成字面意思为信号量,Semaphore可以控同时访问的线程个数,通过acquire()获取
 * 一个许可,如果没有就等待,而release()释放一个许可。

Semaphore类位于java.util.concurrent包下,它提供了2个构造器:

public Semaphore(int permits) {          //参数permits表示许可数目,即同时可以允许多少线程进行访问
    sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {    //这个多了一个参数fair表示是否是公平的,即等待时间越久的越先获取许可
    sync = (fair)? new FairSync(permits) : new NonfairSync(permits);
}

acquire()、release()方法:

public void acquire() throws InterruptedException {  }     //获取一个许可
public void acquire(int permits) throws InterruptedException { }    //获取permits个许可
public void release() { }          //释放一个许可
public void release(int permits) { }    //释放permits个许可

 注意:

acquire()用来获取一个许可,若无许可能够获得,则会一直等待,直到获得许可。

release()用来释放许可。注意,在释放许可之前,必须先获获得许可。

立即得到执行结果的方法:

public boolean tryAcquire() { };    //尝试获取一个许可,若获取成功,则立即返回true,若获取失败,则立即返回false
public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException { };  //尝试获取一个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false
public boolean tryAcquire(int permits) { }; //尝试获取permits个许可,若获取成功,则立即返回true,若获取失败,则立即返回false
public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException { }; //尝试获取permits个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false

例子:图书馆有3本书,但有5名学生预约,一本书只能由一名学术使用,只有使用完了其他人才能使用。

public class MySemaphore {

	public static final int BOOKCOUNT = 3; 
	public static final int STUDENTCOUNT = 5;
	public static void main(String[] args) {		
		ExecutorService executorService = Executors.newFixedThreadPool(10);
		Semaphore semaphore = new Semaphore(BOOKCOUNT); //书本数目
		for (int i = 0; i < STUDENTCOUNT; i++) {
	    	executorService.execute(new MySemaphoreThread(semaphore,i));
		}
	    executorService.shutdown();
	}
	private static class MySemaphoreThread implements Runnable{
        private Semaphore semaphore;
        private int num;
        
		public MySemaphoreThread(Semaphore semaphore, int num) {
			super();
			this.semaphore = semaphore;
			this.num = num;
		}
		@Override
		public void run() {
			// TODO Auto-generated method stub
			synchronized (this) {
				try {
					this.semaphore.acquire();
					System.out.println("学术"+num+"借出一个书本");
					Random random = new Random();
					int randomNum = random.nextInt((3000 - 1000)+1)+1000;//制造1000到3000之间的随机数
					Thread.sleep(randomNum);
					System.out.println("学术"+num+"还回一个书本");
					this.semaphore.release();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		
	}
}

效果:

发布了95 篇原创文章 · 获赞 180 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/weixin_38316697/article/details/89364515