Java并发之Semaphore和Exchanger工具类简单介绍

一、Semaphore介绍

        Semaphore意思为信号量,是用来控制同时访问特定资源的线程数数量。它的本质上其实也是一个共享锁。Semaphore可以用于做流量控制,特别是公用资源有限的应用场景。例如:某个停车场车位只有10个,一开始停车场没有车辆所有车位全部空着,然后先后到来八辆车,停车场车位够,安排进去停车,然后又来三辆,这个时候由于只有两个停车位,所有只能停两辆,其余一辆必须在外面候着,直到停车场有空车位,当然以后每来一辆都需要在外面候着。当停车场有车开出去,里面有空位了,则安排一辆车进去。这个场景就是典型的信号量应用场景。

       我们可以把停车场比喻成Semaphore,然后车辆就是一个一个的线程。车位就是许可数10。当来一辆车的时候许可数就减一,知道许可数减为0。这个时候已经没有停车位了。所以车辆必须排队等候车位。过一段时间有一辆车开走了,这个时候许可数就增加一。同时外面的车就可以进来一辆。是否按照排队次序进来就看是否是公平锁还是非公平锁。

二、SemaphoreAPI使用介绍

       2.1、构造函数:

  1. Semaphore(int permits) :创建具有给定的许可数和非公平的公平设置的 Semaphore。

  2. Semaphore(int permits, boolean fair) :创建具有给定的许可数和给定的公平设置的 Semaphore。

       2.2、信号量获取:

       1.  Semaphore提供了acquire()方法来获取一个许可。

      2.3、信号量释放:

       1.  Semaphore提供release()来释放许可。

      2.4、其他方法:

       1.   intavailablePermits():返回此信号量中当前可用的许可证数。

       2.   intgetQueueLength():返回正在等待获取许可证的线程数。

       3.   booleanhasQueuedThreads():是否有线程正在等待获取许可证。

·      4.   void reducePermits(int reduction):减少reduction个许可证,是个protected方法。

·      5.   Collection getQueuedThreads():返回所有等待获取许可证的线程集合,是个protected方

法。

三、应用实例

  1 
  2 import java.util.concurrent.Semaphore;
  3 
  4 public class SemaphoreUserCase {
  5 
  6     public static void main(String[] args)  {
  7         //停车位一共2个
  8         Parking parking = new Parking(2);
  9         for(int i = 0;i < 5;i++){
 10             new Car(parking).start();
 11         }
 12     }
 13 
 14 }
 15 class Parking {
 16     //信号量
 17     private Semaphore semaphore;
 18     Parking(int count){
 19         //初始化信号量
 20         semaphore = new Semaphore(count);
 21     }
 22     public void park(){
 23         try {
 24             //获取信号量
 25             semaphore.acquire();
 26             long time = (long) (Math.random() * 100);
 27             System.out.println(Thread.currentThread().getName() + "进入停车场,停车" + time + "秒..." );
 28             Thread.sleep(time);
 29             System.out.println(Thread.currentThread().getName() + "开出停车场...");
 30         } catch (InterruptedException e) {
 31             e.printStackTrace();
 32         } finally {
 33             //将信号量递减
 34             semaphore.release();
 35         }
 36     }
 37 }
 38 class Car extends Thread{
 39 
 40     Parking parking;
 41     Car(Parking parking){
 42         this.parking = parking;
 43     }
 44     @Override
 45     public void run() {
 46         //进入停车场停车
 47         parking.park();
 48     }
 49 
 50 }
  1 Thread-0进入停车场,停车2秒...
  2 Thread-2进入停车场,停车58秒...
  3 Thread-0开出停车场...
  4 Thread-3进入停车场,停车45秒...
  5 Thread-3开出停车场...
  6 Thread-4进入停车场,停车11秒...
  7 Thread-2开出停车场...
  8 Thread-4开出停车场...
  9 Thread-1进入停车场,停车95秒...
 10 Thread-1开出停车场...

从上面结果我们可以很容易的看出来,停车场只有两个位置。当被占用的时候,其他车辆只能等待。当信号量为0的时候,线程会被阻塞。

四、Exchanger介绍

       Exchanger意思为交换者,作为Java并发工具类他的作用是交换过个线程中的数据。它提供一个同步点,在这个同步点,两个线程可以交换彼此的数据。当第一个线程调用exchange()方法,那么他会一直等待第二个线程调用exchange()方法。直到交换数据。Exchanger工具类最常用于遗传学上的应用。遗传算法里需要选出两个人作为交配对象,这时候会交换 两人的数据,并使用交叉规则得出2个交配结果。Exchanger也可以用在校对数据。比如我们需要将纸制银流通过人工的方式录入成电子银行流水,为了避免错误,采用AB岗两人进行录入,录入到Excel之后,系统需要加载这两个Excel,并对这两个Excel数据进行校对,看看是否录入的一致。

五、ExchangerAPI介绍

       5.1、构造函数:

  1. Exchanger()创建一个新的 Exchanger

       5.2、其他API:

       1.   exchange(V x) 等待另一个线程到达此交换点(除非当前线程被中断),然后将给定的对象传送给该线程,并接收该线程的对象。

       2. exchange(V x, long timeout, TimeUnit unit) 等待另一个线程到达此交换点(除非当前线程被中断,或者超出了指定的等待时间),然后将给定的对象传送给该线程,同时接收该线程的对象。

六、应用实例

  1 
  2 import java.util.concurrent.Exchanger;
  3 import java.util.concurrent.ExecutorService;
  4 import java.util.concurrent.Executors;
  5 
  6 public class ExchangerUserCase {
  7 
  8     private static final Exchanger<String> exgr = new Exchanger<String>();
  9 
 10     private static ExecutorService threadPool = Executors.newFixedThreadPool(2);
 11 
 12     public static void main(String[] args) {
 13 
 14         threadPool.execute(new Runnable() {
 15             @Override
 16             public void run() {
 17                 try {
 18                     String A = "1000";// A录入银行流水数据
 19                     exgr.exchange(A);
 20                 } catch (InterruptedException e) {
 21                 }
 22             }
 23         });
 24 
 25         threadPool.execute(new Runnable() {
 26             @Override
 27             public void run() {
 28                 try {
 29                     String B = "2000";// B录入银行流水数据
 30                     String A = exgr.exchange("B");
 31                     System.out.println("A和B数据是否一致:" + A.equals(B) + ",A录入的是:"
 32                             + A + ",B录入是:" + B);
 33                 } catch (InterruptedException e) {
 34                 }
 35             }
 36         });
 37 
 38         threadPool.shutdown();
 39 
 40     }
 41 
 42 }
 43 
  1 A和B数据是否一致:false,A录入的是:1000,B录入是:2000
posted @ 2018-05-18 10:36  数月亮  阅读(836)  评论(0编辑  收藏  举报