Java并发编程的艺术之八----java中的并发工具类

1.等待多线程完成的countDownLatch

CountDownLatch允许一个或多个线程等待其他线程完成操作。

线程中,让一个线程等待最简单的做法是使用join方法,线程A中调用B.join方法,说明让线程A等待线程B完成之后再执行。

实现原理:不停检查线程是否存活,如果join线程存活则让当前线程永远等待。Wait(0)表示永远等待下去

直到join线程终止后,线程的this.notifyAll方法被调用

CountDownLatch接受一个int类型的参数作为计数器,如果你想等待n个点完成,这里就传入n。调用countDownLatch的countDown方法时,n就会减1,如果计数器大于0,await方法等待,如果计数器等于零,await方法不等待。countDown方法可以用在任何地方,可以是n个线程,也可以1个线程里n个步骤。

ExecutorService service = Executors.newCachedThreadPool();

       final CountDownLatch cdOrder = new CountDownLatch(1);

       final CountDownLatch cdAnswer = new CountDownLatch(3);

       for(int i = 0; i < 3; i++){

           Runnable runnable = new Runnable(){

              @Override

              public void run() {

                  // TODO Auto-generated method stub

                  try {

                     System.out.println("线程" + Thread.currentThread().getName() +

                         "正在准备接受命令");

                     cdOrder.await();

                     System.out.println("线程" + Thread.currentThread().getName() +

                            "已接收命令");

                     Thread.sleep((long)Math.random()*1000);

                     System.out.println("线程" + Thread.currentThread().getName() +

                            "回应命令处理结果");

                     cdAnswer.countDown();

                  } catch (InterruptedException e) {

                     // TODO Auto-generated catch block

                     e.printStackTrace();

                  }

              }

           };

           service.execute(runnable);

       }

       try{

           Thread.sleep((long)Math.random()*1000);

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

      

2.同步屏障CyclicBarrier

可循环使用的屏障,让一组线程到达一个屏障(同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行。

假设CyclicBarrier=3,如果阻塞没有到达三个,那么await方法就会一直等待,如果到达三个,那么就不会阻塞。

应用场景:

接受指令,有三个线程,需要到达指定地点集合,第一个线程先到,调用await方法,第二个线程也是一样,等到第三个线程到了,最后一个线程到达屏障,则await开始执行。

CyclicBarrier和CountDownLatch的区别

CountDownLatch的计数器只能用一次,而CyclicBarrier计数器可以使用reset重置,其实不用手动重置,await到达最后一个的时候,就会自动将计数器置为初始化个数,CyclicBarrier能处理更为复杂的业务,

CyclicBarrier还提供其他有用的方法,比如getNumberWaiting方法可以获得Cyclic-Barrier阻塞的线程数量。isBroken()方法用来了解阻塞的线程是否被中断

3.控制并发线程数的semaphore

控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源

* semaphore实现的功能就类似厕所有5个坑,假如有十个人要上厕所

 * ,那么同事能有多少个人去上厕所呢,同时只有5个人能够占用,当5个人

 * 中的任何一个人让开后,其中在等待的另外5个人中又有一个人可以占用了

 *

 * 另外等待的5个人中可以随机获得优先机会,可以是按照先来后到的顺序获得机会

 * ,这取决于构造semaphore对象时传入的参数选项

 * final Semaphore sp = new Semaphore(3, true);true表示按照先来后来顺序

 *

 *单个信号量的semaphore对象可以实现互斥锁的功能,并且可以由一个线程

 *获得锁,另一个线程释放锁,应用于死锁回复的场合

 

public class SemaphoreTest {

    public static void main(String[] args) {

       ExecutorService service = Executors.newCachedThreadPool();

       final Semaphore sp = new Semaphore(3);

       for(int i = 0; i < 10; i++){

           Runnable runnable = new Runnable(){

              public void run(){

                  try{

                     sp.acquire();

                  }catch(InterruptedException e1){

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

       }

    }

}

首先线程使用Semaphore的acquire()方法获取一个许可证,如果semaphore小于0,就阻塞使用完之后调用release()方法归还许可证。

4.线程间交换数据的Exchanger

它提供一个同步点,在这个同步点,两个线程可以交换彼此的数据。这两个线程通过exchange方法交换数据,如果第一个线程先执行exchange()方法,它会一直等待第二个线程也执行exchange方法,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产出来的数据传递给对方

public static void main(String[] args) {

       ExecutorService service = Executors.newCachedThreadPool();

       final Exchanger exchanger = new Exchanger();

       service.execute(new Runnable(){

           @Override

           public void run() {

              // TODO Auto-generated method stub

              try{

                  String data1 = "zxx";

                  System.out.println("线程" + Thread.currentThread().getName() +

                         "正在把数据" + data1 + "换出去");

                  Thread.sleep((long) (Math.random()*1000));

                  String data2 = (String) exchanger.exchange(data1);

                  System.out.println("线程" + Thread.currentThread().getName() +

                         "换回的数据为" + data2);

              }catch(Exception e){

                  e.printStackTrace();

              }

           }

       });

      

       service.execute(new Runnable(){

           @Override

           public void run() {

              // TODO Auto-generated method stub

              try{

                  String data1 = "lhm";

                  System.out.println("线程" + Thread.currentThread().getName() +

                         "正在把数据" + data1 + "换出去");

                  Thread.sleep((long) (Math.random()*1000));

                  String data2 = (String) exchanger.exchange(data1);

                  System.out.println("线程" + Thread.currentThread().getName() +

                         "换回的数据为" + data2);

              }catch(Exception e){

                  e.printStackTrace();

              }

           }

       });

      

       service.shutdown();

      

    }

 

猜你喜欢

转载自blog.csdn.net/huangwei18351/article/details/82975489