• 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
  • 相关阅读:
    [Laravel] mac下通过 homestead 搭建环境 到运行项目
    Mac下Laravel的Homestead环境配置
    Window10 下安装 Laravel / Homestead 视频
    Windows 10下Laravel的开发环境安装及部署(Vagrant + Homestead)
    Apache 调用不同的 PHP 版本
    inux系统用户名和全名有什么区别
    oracle 学习day01
    rpm
    Linux下可以使用ps命令来查看Oracle相关的进程
    oracle 建用户
  • 原文地址:https://www.cnblogs.com/gemine/p/9054985.html
Copyright © 2020-2023  润新知