看完后,你再也不用怕面试问并发编程啦(14)

  • 获取许可,acquire()方法获取许可,再获取许可前处于阻塞等待 。
  • 方法说明Semaphore(int permits)permits是允许同时运行的线程数目,创建指定数据线程的信号量Semaphore(int permits, boolean fair)permits是允许同时运行的线程数目,创建指定数据线程的信号量;fair指定是公平模式还是非公平模式,默认非公平模式void acquire()方法阻塞,直到申请获取到许可证才可以运行当前线程void release()释放当前线程一个阻塞的 acquire() 方法,方法增加一个许可证intavailablePermits()返回此信号量中当前可用的许可证数intgetQueueLength()返回正在等待获取许可证的线程数booleanhasQueuedThreads()是否有线程正在等待获取许可证void reducePermits(int reduction)减少reduction个许可证,是个protected方法Collection getQueuedThreads()返回所有等待获取许可证的线程集合,是个protected方法 代码:package cn.itcast.thread;import java.util.concurrent.Semaphore;import java.util.concurrent.TimeUnit;public class Test18 {public static void main(String[] args) {// 1. 创建信号量对象控制并发线程数量 , 设置许可数5个(同时运行5个线程)Semaphore semaphore = new Semaphore(5, true);// 2. 循环运行10个线程(会看到每次只允许5个线程)for (int i = 0; i < 10; i++) {new Thread(() -> {try {// 2.1 申请获取许可semaphore.acquire();// 2.2 运行业务System.out.println(Thread.currentThread().getName() + "车,进入停车场");TimeUnit.SECONDS.sleep(3);// 让当前线程休眠(让线程多运行一会,方便观察效果)System.out.println(Thread.currentThread().getName() + "车,离开停车场");// 2.3 释放阻塞,增加一个许可(让下一个阻塞的线程运行)semaphore.release();} catch (Exception e) {e.printStackTrace();}}).start();}}} 运行效果
    看完后,你再也不用怕面试问并发编程啦

    文章插图
    特点
    Semaphore 在计数器不为 0 的时候对线程就放行,一旦达到 0,那么所有请求资源的新线程都会被阻塞,包括增加请求到许可的线程,Semaphore 是不可重入的 。
    • 每一次请求一个许可都会导致计数器减少 1,同样每次释放一个许可都会导致计数器增加 1,一旦达到 0,新的许可请求线程将被挂起 。
    Semaphore 有两种模式,公平模式 和 非公平模式(默认使用)
    • 公平模式就是调用 acquire 的顺序就是获取许可证的顺序,遵循 FIFO 。
    Semaphore semaphore = new Semaphore(许可数, true); // 公平模式
    • 非公平模式是抢占式的,也就是有可能一个新的获取线程恰好在一个许可证释放时得到了这个许可证 , 而前面还有等待的线程 。
    Semaphore semaphore = new Semaphore(许可数, false); // 非公平模式,(默认) 小结
    • Semaphore 的使用(3个操作)
         – 初始化许可集
         – 增加许可,release()方法释放一个阻塞,增加一个许可 。
         – 获取许可,acquire()方法获取许可 , 再获取许可前处于阻塞等待 。20、并发工具类:Exchanger(交换数据)目标:掌握Exchanger的使用
    介绍
    Exchanger(交换者)是一个用于线程间协作的工具类 , 可以用于进行线程间的数据交换 。
    交换数据原理
    • Exchanger提供一个同步点 , 在这个同步点两个线程可以交换彼此的数据 。
    • 两个线程通过 exchange() 方法交换数据 。
    • 第一个线程先执行 exchange() 方法,会一直等待第二个线程也执行exchange()到达同步点时,两个线程交换数据,将本线程生产出来的数据传递给对方 。
    • 使用 Exchanger 的重点是用对的线程使用 exchange() 方法 。

    看完后,你再也不用怕面试问并发编程啦

    文章插图
    API方法 方法说明V exchange(V x)用于进行线程间的数据交换V exchange(V x, long timeout, TimeUnit unit)设置交换数据并等待超时时间 代码:package cn.itcast.thread;import java.util.concurrent.Exchanger;public class Test19 {public static void main(String[] args) {// 1. 创建交换数据对象,并设置传输数据的类型Exchanger<String> exchanger = new Exchanger<>();// 2. 启动2个线程进行交换数据// 创建线程1new Thread(() -> {// 2.1 定义交换的数据String girl1 = "【柳岩】";System.out.println(Thread.currentThread().getName() + "说:我的女友 " + girl1);System.out.println(Thread.currentThread().getName() + "说:等待线程2交换数据");// 2.2 将数据交换给线程2,并拿到线程2的数据try {String b = exchanger.exchange(girl1);//注意:如果线程2没有到达同步点,当前线程会被阻塞一直等到//成功获取线程2的数据后System.out.println(Thread.currentThread().getName() + "说:我拿到了 " + b);} catch (InterruptedException e) {e.printStackTrace();}}).start();// 创建线程2new Thread(()->{// 2.3 定义交换的数据String girl2 = "【杨幂】";System.out.println(Thread.currentThread().getName()+"说:我的女友 " + girl2);System.out.println(Thread.currentThread().getName()+"说:等待线程1交换数据");// 2.4 将数据交换给线程1,并拿到线程1的数据try {String a = exchanger.exchange(girl2);// 成功获取线程1的数据后System.out.println(Thread.currentThread().getName()+"说:我拿到了 " + a);} catch (InterruptedException e) {e.printStackTrace();}}).start();}}


    推荐阅读