多线程同步工具CountdownLatch,CyclicBarrier,Exchanger,Semaphore

一、CountdownLatch

  • 允许一个或多个线程等待直到在其他线程中执行的一组操作完成。

示例代码:

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);

		//开启3个子线程
		for(int i=0;i<3;i++){
			Runnable runnable = new Runnable(){
					public void run(){
					try {
						System.out.println("线程" + Thread.currentThread().getName() + "正准备接受命令");
						//3个线程阻塞,等cdOrder.countDown()减到0,然后再继续执行
						cdOrder.await();
						System.out.println("线程" + Thread.currentThread().getName() + "已接受命令");
						Thread.sleep((long)(Math.random()*10000));	
						System.out.println("线程" + Thread.currentThread().getName() + "回应命令处理结果");
						//3个线程依次减一
						cdAnswer.countDown();
					} catch (Exception e) {
						e.printStackTrace();
					}				
				}
			};
			service.submit(runnable);
		}		
		try {
			Thread.sleep((long)(Math.random()*10000));
		
			System.out.println("线程" + Thread.currentThread().getName() + "即将发布命令");
			cdOrder.countDown();
			System.out.println("线程" + Thread.currentThread().getName() + "已发送命令,正在等待结果");
			//阻塞,等cdAnswer.countDown()减到0
			cdAnswer.await();
			System.out.println("线程" + Thread.currentThread().getName() + "已收到所有响应结果");
		} catch (Exception e) {
			e.printStackTrace();
		}				
		service.shutdown();
	}
}
  • a CountDownLatch用给定的计数初始化。 await方法阻塞,直到由于countDown()方法的调用而导致当前计数达到零,之后所有等待线程被释放, 并且任何后续的await 调用立即返回。 这是一个一次性的现象 - 计数无法重置。 如果您需要重置计数的版本,请考虑使用CyclicBarrier 。

二、CyclicBarrier

允许一组线程全部等待彼此达到共同屏障点。可以循环执行

示例代码:

public class CyclicBarrierTest {

	public static void main(String[] args) {
		ExecutorService service = Executors.newCachedThreadPool();

		final  CyclicBarrier cb = new CyclicBarrier(3);
		for(int i=0;i<3;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.submit(runnable);
		}
		service.shutdown();
	}
}

三、Exchanger交换器

  • 线程可以在成对内 配对和交换元素的同步点。 每个线程在输入exchange方法时提供一些对象,与合作者线程匹配,并在返回时接收其合作伙伴的对象。
  • 交换器可以被视为一个的双向形式SynchronousQueue 。交换器在诸如遗传算法和管道设计的应用中可能是有用的。

示例代码:

public class ExchangerTest {

    public static void main(String[] args) {
        ExecutorService service = Executors.newCachedThreadPool();
        final Exchanger exchanger = new Exchanger();
        service.submit(new Runnable(){
            public void run() {
                try {

                    String data1 = "zxx";
                    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.submit(new Runnable(){
            public void run() {
                try {

                    String data1 = "lhm";
                    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){

                }
            }
        });
    }
}

四、Semaphore 信号灯

  • 互斥是同时只能一个进行操作,与互斥不同,信号灯允许多个线程同时进行操作
  • 信号量通常用于限制线程数,而不是访问某些(物理或逻辑)资源。 例如,这是一个使用信号量来控制对一个项目池的访问的类

示例代码:

public class SemaphoreTest {
    public static void main(String[] args) {
        ExecutorService service = Executors.newCachedThreadPool();
        Semaphore sp = new Semaphore(3);//指定信号灯数量
        for (int i = 0; i <10 ; i++) {
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        sp.acquire();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程"+Thread.currentThread().getName()+"正在运行,当前有"+(3-sp.availablePermits())+"个在并行");
                    try {
                        Thread.sleep((long)(Math.random()*1000));
                    } 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);
        }
        service.shutdown();
    }
}

你可能感兴趣的:(JAVA基础)