1.Exchanger
方法摘要:
V | exchange(V x) 等待另一个线程到达此交换点(除非当前线程被中断),然后将给定的对象传送给该线程,并接收该线程的对象。 |
V | exchange(V x, long timeout, TimeUnit unit) 等待另一个线程到达此交换点(除非当前线程被中断,或者超出了指定的等待时间),然后将给定的对象传送给该线程,同时接收该线程的对象。 |
功能:用于线程间数据的交换
应用场景:1)遗传算法,目前还不是特别理解 2)校对工作,假设A,B线程做同一件任务,可以通过数据校验判断两线程是否正确的工作
private java.util.concurrent.Exchanger<String> exchanger;
private String userName;
private String phone;
public ExchangerTest(Exchanger<String> exchanger, String userName, String phone){
this.exchanger=exchanger;
this.userName=userName;
this.phone=phone;
}
@Override
public void run() {
try {
System.out.println(exchanger.exchange(userName)+":"+phone);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Exchanger<String> exchanger=new Exchanger<>();
ExchangerTest exchangerTest=new ExchangerTest(exchanger,"你的姓名","0101");
ExchangerTest exchangerTest2=new ExchangerTest(exchanger,"你的代号","ming");
new Thread(exchangerTest).start();
new Thread(exchangerTest2).start();
}
2.Semaphore
void | acquire() 从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。 |
void | acquire(int permits) 从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞,或者线程已被中断。 |
void | acquireUninterruptibly() 从此信号量中获取许可,在有可用的许可前将其阻塞。 |
void | acquireUninterruptibly(int permits) 从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞。 |
int | availablePermits() 返回此信号量中当前可用的许可数。 |
int | drainPermits() 获取并返回立即可用的所有许可。 |
protected Collection<Thread> | getQueuedThreads() 返回一个 collection,包含可能等待获取的线程。 |
int | getQueueLength() 返回正在等待获取的线程的估计数目。 |
boolean | hasQueuedThreads() 查询是否有线程正在等待获取。 |
boolean | isFair() 如果此信号量的公平设置为 true,则返回 true 。 |
protected void | reducePermits(int reduction) 根据指定的缩减量减小可用许可的数目。 |
void | release() 释放一个许可,将其返回给信号量。 |
void | release(int permits) 释放给定数目的许可,将其返回到信号量。 |
String | toString() 返回标识此信号量的字符串,以及信号量的状态。 |
boolean | tryAcquire() 仅在调用时此信号量存在一个可用许可,才从信号量获取许可。 |
boolean | tryAcquire(int permits) 仅在调用时此信号量中有给定数目的许可时,才从此信号量中获取这些许可。 |
boolean | tryAcquire(int permits, long timeout, TimeUnit unit) 如果在给定的等待时间内此信号量有可用的所有许可,并且当前线程未被中断,则从此信号量获取给定数目的许可。 |
boolean | tryAcquire(long timeout, TimeUnit unit) 如果在给定的等待时间内,此信号量有可用的许可并且当前线程未被中断,则从此信号量获取一个许可。 |
功能:控制同时访问特定资源的线程数量
应用场景:流量控制,比如数据库的连接
Semaphore主要方法:
Semaphore(int permits):构造方法,创建具有给定许可数的计数信号量并设置为非公平信号量。
Semaphore(int permits,boolean fair):构造方法,当fair等于true时,创建具有给定许可数的计数信号量并设置为公平信号量。
void acquire():从此信号量获取一个许可前线程将一直阻塞。相当于一辆车占了一个车位。
void acquire(int n):从此信号量获取给定数目许可,在提供这些许可前一直将线程阻塞。比如n=2,就相当于一辆车占了两个车位。
void release():释放一个许可,将其返回给信号量。就如同车开走返回一个车位。
void release(int n):释放n个许可。
int availablePermits():当前可用的许可数。
序号 | 名称 | 类型 | 含义 |
---|---|---|---|
1 | corePoolSize | int | 核心线程池大小 |
2 | maximumPoolSize | int | 最大线程池大小 |
3 | keepAliveTime | long | 线程最大空闲时间 |
4 | unit | TimeUnit | 时间单位 |
5 | workQueue | BlockingQueue<Runnable> | 线程等待队列 |
6 | threadFactory | ThreadFactory | 线程创建工厂 |
7 | handler | RejectedExecutionHandler | 拒绝策略 |
就是在该类初始化的时候,给定一个数字A,每个线程调用acquire()方法后,
首先判断A是否大于0,如果大于0,就将A减去1,然后执行对应的线程,如果不大于0,那么就会阻塞,
直到其他线程调用了release()方法,将A加上1,该线程可能有执行的机会.
场景介绍:
有一个停车场只有5个车位,现在有100辆车要去抢这个5个车位,
理想情况下最多只有五辆车同时可以抢到车位,那么没有抢到车位的车只能等到,其他的车让出车位,才有机会去使用该车位。
public static void main(String[] args) {
//阻塞队列
BlockingQueue<String> parks = new LinkedBlockingQueue<>(5);
parks.offer("车位一");
parks.offer("车位二");
parks.offer("车位三");
parks.offer("车位四");
parks.offer("车位五");
ExecutorService executorService = Executors.newCachedThreadPool();
//如博文中所说的初始值为5, 专业的说法就是5个许可证
Semaphore semaphore = new Semaphore(2);
for (int i = 0; i < 100; i++) {
final int no = i;
Thread t1 = new Thread(() -> {
try {
/**
* 获取许可,首先判断semaphore内部的数字是否大于0,如果大于0,
* 才能获得许可,然后将初始值5减去1,线程才会接着去执行;如果没有
* 获得许可(原因是因为已经有5个线程获得到许可,semaphore内部的数字为0),
* 线程会阻塞直到已经获得到许可的线程,调用release()方法,释放掉许可,
* 也就是将semaphore内部的数字加1,该线程才有可能获得许可。
*/
semaphore.acquire();
/**
* 对应的线程会到阻塞对,对应车辆去获取到车位,如果没有拿到一致阻塞,
* 直到其他车辆归还车位。
*/
String park = parks.take();
System.out.println("车辆【" + no + "】获取到: " + park);
Thread.sleep((long) Math.random() * 6000);
semaphore.release(); //线程释放掉许可,通俗来将就是将semaphore内部的数字加1
parks.offer(park); //归还车位
System.out.println("车辆【" + no + "】离开 " + park);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
executorService.execute(t1);
}
}
3.CyclicBarrier
int | await() 在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。 |
int | await(long timeout, TimeUnit unit) 在所有参与者都已经在此屏障上调用 await 方法之前将一直等待,或者超出了指定的等待时间。 |
int | getNumberWaiting() 返回当前在屏障处等待的参与者数目。 |
int | getParties() 返回要求启动此 barrier 的参与者数目。 |
boolean | isBroken() 查询此屏障是否处于损坏状态。 |
void | reset() 将屏障重置为其初始状态。 |
功能:控制同时访问特定资源的线程数量
应用场景:希望创建一组任务,并行执行,在进行下一个步骤之前等待,直到所有任务完成
static class TaskThread extends Thread{
CyclicBarrier cyclicBarrier;
public TaskThread(CyclicBarrier cyclicBarrier){
this.cyclicBarrier=cyclicBarrier;
}
@Override
public void run() {
try {
Thread.sleep(1000);
System.out.println(getName()+"到达栅栏 A");
cyclicBarrier.await();
System.out.println(getName()+"冲破栅栏 A");
Thread.sleep(1000);
System.out.println(getName()+"到达栅栏 B");
cyclicBarrier.await();
System.out.println(getName()+"冲破栅栏 B");
} catch (InterruptedException e) {
e.printStackTrace();
}catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
int number=3;
CyclicBarrier cyclicBarrier1=new CyclicBarrier(number, new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"完成最后任务");
}
});
for (int i = 0; i <number; i++) {
new TaskThread(cyclicBarrier1).start();
}
}
}
4. CyclicBarrier 与 CountDownLatch 区别
- CountDownLatch 是一次性的,CyclicBarrier 是可循环利用的
- CountDownLatch 参与的线程的职责是不一样的,有的在倒计时,有的在等待倒计时结束。CyclicBarrier 参与的线程职责是一样的。
-
4.CountDownLatch
方法区:
void | await() 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。 |
boolean | await(long timeout, TimeUnit unit) 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。 |
void | countDown() 递减锁存器的计数,如果计数到达零,则释放所有等待的线程。 |
long | getCount() 返回当前计数。 |
String | toString() 返回标识此锁存器及其状态的字符串。 |
CountDownLatch(int count) //实例化一个倒计数器,count指定计数个数
countDown() // 计数减一
await() //等待,当计数减到0时,所有线程并行执行
功能:允许一个或多个线程等待其他线程完成操作
-
static final CountDownLatch countdownLatch=new CountDownLatch(10);
static final CountDownLatchTest countDownLatchTest=new CountDownLatchTest(); -
@Override
public void run() {
try {
Thread.sleep(new Random().nextInt(10)*6000);
System.out.println("开始检查发射配置");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
countdownLatch.countDown();//加一操作
}
}
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService=Executors.newFixedThreadPool(10);
for (int i = 0; i <10 ; i++) {
executorService.submit(countDownLatchTest);
}
countdownLatch.await();//等待全部完成
System.out.println("火箭发射");
executorService.shutdown();//关闭线程
}