java.util.concurrent 工具包的使用(二)
Exchanger ( 交换器 )
-----作用-----
java.util.concurrent包中的Exchanger类可用于两个线程之间交换信息。可简单地将Exchanger对象理解为一个包含两个格子的容器,通过exchanger方法可以向两个格子中填充信息。当两个格子中的均被填充时,该对象会自动将两个格子的信息交换,然后返回给线程,从而实现两个线程的信息交换。
-----构造函数-----
构造函数 | 作用 | 备注 |
---|---|---|
public Exchanger() | 构造一个Exchanger对象 |
-----常用API-----
API | 作用 | 备注 |
---|---|---|
public V exchange(V x) throws InterruptedException | 等待与另外的线程交换一个对象 | 是个阻塞方法,可,可中断 |
public V exchange(V x, long timeout, TimeUnit unit) | 用第一个,有时间限制,会超时 | 再2个线程交换的时候,如果一个线程提前结束,可能会导致另外一个线程死等待 |
-----代码示例-----
-
Example 1 最简单的交换 Demo
package myConcurrent.s2020_03_27; import java.util.concurrent.Exchanger; public class ExchangerExample1 { public static void main(String[] args) { final Exchanger<String> exchanger = new Exchanger<>(); new Thread(() -> { try { String call = "我是AAAA的消息"; System.out.println(Thread.currentThread().getName() + " 发送了 >>> " + call); String str = exchanger.exchange(call); System.out.println(Thread.currentThread().getName() + " 收到了 >>> " + str); } catch (InterruptedException e) { e.printStackTrace(); } },"AAAAAA").start(); new Thread(() -> { try { String call = "我是BBBB的消息"; System.out.println(Thread.currentThread().getName() + " 发送了 >>> " + call); String str = exchanger.exchange(call); System.out.println(Thread.currentThread().getName() + " 收到了 >>> " + str); } catch (InterruptedException e) { e.printStackTrace(); } },"BBBBB").start(); } }
-
Example 2测试有时间限制的方法 (注意 一旦一个设置了 超时延迟,要把另一个也要设,否则会有死等待的可能 )
package myConcurrent.s2020_03_27; import java.util.concurrent.Exchanger; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * 有时间限制 * */ public class ExchangerExample2 { public static void main(String[] args) throws InterruptedException { final Exchanger<String> exchanger = new Exchanger<>(); new Thread(() -> { try { String str = exchanger.exchange("ssdf",1, TimeUnit.SECONDS); System.out.println(str); } catch (InterruptedException e) { e.printStackTrace(); } catch (TimeoutException e) { System.out.println("超时了!"); e.printStackTrace(); } }).start(); TimeUnit.SECONDS.sleep(2); new Thread(() -> { try { String str = exchanger.exchange("ssdf",1, TimeUnit.SECONDS); System.out.println(str); } catch (InterruptedException e) { e.printStackTrace(); } catch (TimeoutException e) { System.out.println("超时了!"); e.printStackTrace(); } }).start(); } }
-
由于交换,涉及了对象的发布,可能会导致一些线程安全的问题,因此对象的交换要和原子引用结合在一起
package myConcurrent.s2020_03_27; import java.util.concurrent.Exchanger; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; public class ExchangerExample3 { public static void main(String[] args) throws InterruptedException { final Exchanger<UnSafeObject> exchanger = new Exchanger<>(); new Thread(() -> { UnSafeObject adf = new UnSafeObject("adf", 12); AtomicReference<UnSafeObject> reference = new AtomicReference<>(adf); try { UnSafeObject exchange = exchanger.exchange(reference.get()); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); TimeUnit.SECONDS.sleep(1); new Thread(() -> { UnSafeObject object = new UnSafeObject("abc",1); AtomicReference<UnSafeObject> reference = new AtomicReference<>(object); try { UnSafeObject exchange = exchanger.exchange(reference.get()); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); } static class UnSafeObject { private String name; private int age; public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } public UnSafeObject(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } } }
Semaphore ( 信号量 )
-----作用-----
- 它是一个计数信号量
- 一个更贴切的称呼----许可证管理器
- 拥有这一组许可证
-----构造函数-----
构造函数 | 作用 | 备注 |
---|---|---|
public Semaphore(int permits) | 构造一个拥有permits个的许可证的管理器 | permits并没有做什么限制,负数也是可以的 |
public Semaphore(int permits, boolean fair) | 同一,另一个参数是 是否 公平,如果是公平,尽量会使每个线程得到相同的抢占数量 | 使用第一种构造,默认是不公平的 |
-----常用API-----
API | 作用 | 备注 |
---|---|---|
public void acquire() throws InterruptedException | 向管理器要一个许可证,如果没有了,就会陷入阻塞 | |
public void acquire(int permits) throws InterruptedException | 作用和第一个方法一样,就是向管理器要permits个许可证 | |
public void acquireUninterruptibly() | 作用和第一个方法一样,但是不会响应中断信号 | |
public void acquireUninterruptibly(int permits) | 同第3个方法,申请的数量不同 | |
public boolean tryAcquire() | 尝试向管理器要一个许可证,如果没有,就不要了.不会陷入阻塞,一种尝试 | |
public boolean tryAcquire(int permits) | 尝试向管理器要permits个许可证,如果没有,就不要了 | |
public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException | 尝试一段时间向管理器要一个许可证,如果没有,就不要了,会有短暂的阻塞(能够响应中断) | 返回值就是有没有抢到 |
public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException | 同上 | |
public void release() | 向管理器归还一个许可证 | |
public void release(int permits) | 向管理器归还permits个许可证 | permits小于0会报错 |
public int availablePermits() | 精确的获得当前管理器还有多少个许可证 | 可以用来实时监控这个管理器 |
public int drainPermits() | 获取当前管理器的所有许可证 | 返回值是获得了多少个许可证 |
public final int getQueueLength() | 获得正在等待获取许可证的线程数量 | 这是个估计值,不是十分精确,可是用来监控管理器用的方法 |
-----代码示例-----
-
Example 1 测试了基本的使用 以及 一个 监控 管理器的实现
package myConcurrent.s2020_03_27; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.stream.IntStream; public class SemaphoreExample1 { public static void main(String[] args) throws InterruptedException { final Semaphore semaphore = new Semaphore(-1); //获取许可证超过时间的例子 boolean flag = semaphore.tryAcquire(2, TimeUnit.SECONDS); System.out.println("是否获得了许可证 : " + flag); //给管理器补充一些许可证 semaphore.release(11); //现在拥有10个许可证 System.out.println("现在拥有个 " + semaphore.availablePermits() + " 可用的许可证"); IntStream.range(0, 100).forEach(i -> { new Thread(() -> { try { semaphore.acquire(); Thread.sleep(1000); System.out.println(i + "完成了工作!--"); } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); //归还许可证 } }).start(); }); Thread monitor = new Thread(() -> { while (true){ int queueLength = semaphore.getQueueLength(); int number = semaphore.availablePermits(); System.out.println("==================================\n" + "当前有" + queueLength + "个线程正在等待获取许可证\n" + "当前有" + number + "个可用的许可证\n" + "=============================="); try { Thread.sleep(1_000); } catch (InterruptedException ignore) { break; } } }); monitor.start(); Thread.sleep(20_000); monitor.interrupt(); } }
感谢观看
记录萌新的学习经历
欢迎大佬来拍砖