一、信号量 Semaphore
信号量用于控制并发访问某个共享资源的线程数量,常用于限流。
使用时将信号量初始化为最大许可数量,信号量通过该值控制线程并发访问的数量。线程访问共享资源前先调用acquire()申请访问许可,如果申请成功,则该线程可以访问共享资源。如果申请失败,则该线程暂时等待。线程释放共享资源时需要调用release()返还访问许可。
常用方法
// 构造函数,permits表示最大许可数量
public Semaphore(int permits)
// 构造函数,fair表示是否公平,即等待时间越久的线程优先获取许可
public Semaphore(int permits, boolean fair)
// 申请一个许可,如果没有许可,线程将等待
public void acquire()
// 申请permits个许可,如果没有许可,线程将等待
public void acquire(int permits)
// 返还一个许可
public void release()
// 返还permits个许可
public void release(int permits)
// 尝试申请一个许可,如果没有许可则返回false
public boolean tryAcquire()
使用示例
public class SemaphoreDemo {
// 允许最高5个线程并发
private static final int MAX_AVAILABLE = 5;
private static Semaphore semaphore = new Semaphore(MAX_AVAILABLE);
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(5*MAX_AVAILABLE);
for (int i = 0; i < 10*MAX_AVAILABLE; i++) {
pool.execute(new Runnable() {
public void run() {
try {
// 申请一个许可
semaphore.acquire();
System.out.println("第" + i + "个线程申请到共享资源");
Thread.sleep(3000);
System.out.println("第" + i + "个线程返还了共享资源");
// 返还一个许可
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
pool.shutdown();
}
}
二、倒计数器 CountDownLatch
俗称闭锁,它允许一个或多个线程一直等待,直到其他线程都执行完后再执行。
它是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就减1,当计数器的值为0时,表示所有线程都执行完毕,然后等待在倒计数器上的线程就可以恢复执行了。
常用方法
// 实例化一个倒计数器,count指定计数个数
public CountDownLatch(int count)
//调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
public void await()
//将count值减1
public void countDown()
使用示例
public class CountDownLatchDemo {
public static void main(String[] args) {
System.out.println("主线程开始执行");
final CountDownLatch latch = new CountDownLatch(50);
ExecutorService pool = Executors.newCachedThreadPool();
for (int i = 0; i < 50; i++) {
pool.execute(new Runnable() {
@Override
public void run() {
try {
System.out.println("子线程:"+Thread.currentThread().getName()+"执行");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 子线程执行完毕,计数器减一
latch.countDown();
}
});
}
pool.shutdown();
System.out.println("主线程等待50个子线程执行完毕");
try {
// 主线程等待在latch上
latch.await();
System.out.println("50个子线程都执行完毕,主线程恢复执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
三、循环栅栏 CyclicBarrier
循环栅栏可以使一组线程到达一个同步点处等待,直到所有线程都到达同步点后,栅栏打开,所有线程恢复执行,同时栅栏将被重置。
常用方法
// 构造函数,parties表示让多少个线程等待在同步点。
public CyclicBarrier(int parties)
// 构造函数,barrierAction表示所有线程到达同步点后先执行barrierAction,再打开栅栏。
public CyclicBarrier(int parties, Runnable barrierAction)
// 线程调用await()表示自己已经到达同步点。
public int await()
// 带超时时间的await
public int await(long timeout, TimeUnit unit)
使用示例
class Writer extends Thread {
private CyclicBarrier cyclicBarrier;
public Writer(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName()+"正在写入数据...");
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName()+"写入数据完毕,等待其他线程");
// 同步点,等待其他线程到达
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
}catch(BrokenBarrierException e){
e.printStackTrace();
}
}
}
public class CyclicBarrierDemo {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new Runnable() {
@Override
public void run() {
System.out.println("所有写操作执行完毕,下面继续执行读操作...");
System.out.println(Thread.currentThread().getName()+"正在读数据...");
}
});
System.out.println("所有线程开始执行写操作...");
for(int i = 0;i < 5; i++) new Writer(cyclicBarrier ).start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("cyclicBarrier重用...");
System.out.println("所有线程开始执行写操作...");
for(int i = 0;i < 5; i++) new Writer(cyclicBarrier ).start();
}
}
四、阶段协同器 Phaser
阶段协同器用于控制多个线程分阶段协作完成任务。
Phaser把任务划分为多个阶段,每个阶段可以有任意个线程参与,可以在运行时随时注册或注销线程到某个阶段。Phaser支持层次化结构,子Phaser可以作为一个参与者加入到父Phaser中,当子Phaser中没有线程参与时,将自动从父Phaser中注销。Phaser内部维护了一个计数器,用来控制阶段需要等待的线程数量。调用register()方法或arriveAndDeregister()方法可以动态的增减计数器的值。线程完成阶段任务后需要调用arrive系列方法告知Phaser自己已到达(阶段终点)。当一个阶段中所有注册的线程都到达后,Phaser的OnAdvance()方法将被回调。然后Phaser释放等待线程进入下个阶段。如此循环,直到结束。Phaser内部维护了一个阶段号,初始值为0。每当所有注册的线程都到达一个阶段时,阶段号加1。
常用方法
// 构造函数,初始化阶段号为0
public Phaser()
// 构造函数,初始化阶段号为0,parties指定计数器的值
public Phaser(int parties)
// 构造函数,效果同Phaser(parent, 0)
public Phaser(Phaser parent)
// 构造函数,parent指定父Phaser,parties指定计数器的值
public Phaser(Phaser parent, int parties)
// 注册一个线程到本阶段及后面的阶段(计数器加一),返回当前阶段号
public int register()
// 注册parties个线程到本阶段及后面的阶段(计数器加parties),返回当前阶段号
public int bulkRegister(int parties)
// 告知到达,然后不等待当前阶段下其他线程,直接向下一个阶段前进。返回当前阶段号,如果Phaser已经终止,则返回负数
public int arrive()
// 告知到达,然后等待当前阶段下其他线程到达。返回到达阶段号,如果Phaser已经终止,则返回负数
public int arriveAndAwaitAdvance()
// 告知到达,然后从本阶段起注销一个线程(计数器减一),然后不等待当前阶段下其他线程,直接向下一个阶段前进。返回当前阶段号。
public int arriveAndDeregister()
// 在指定阶段等待
public int awaitAdvance(int phase)
// 阶段执行完成后回调此方法,可以覆盖此方法来定义阶段到达动作,phase表示阶段号,此方法返回true将终结Phaser对象
protected boolean onAdvance(int phase, int registeredParties)
// 获取当前阶段号
public final int getPhase()
// 强制终止,此后Phaser对象将不可用,register等将不再有效
public void forceTermination()
// 判断是否终止
public boolean isTerminated()
使用示例
class MyPhaser extends Phaser {
@Override
// 每个阶段完成后回调此方法
protected boolean onAdvance(int phase, int registeredParties) {
switch (phase) {
case 0:
System.out.println("阶段0结束了");
return false;
case 1:
System.out.println("阶段1结束了");
return false;
case 2:
System.out.println("阶段2结束了");
return false;
case 3:
System.out.println("阶段3结束了");
return false;
default:
System.out.println("整体结束了");
return true;
}
}
}
class Task extends Thread {
private Phaser phaser;
Task(Phaser phaser) {
this.phaser = phaser;
}
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + "阶段0的任务开始了");
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + "阶段0的任务完成了");
// 到达并在此等待其他线程到达
phaser.arriveAndAwaitAdvance();
System.out.println(Thread.currentThread().getName() + "阶段1的任务开始了");
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + "阶段1的任务完成了");
// 到达并在此等待其他线程到达
phaser.arriveAndAwaitAdvance();
System.out.println(Thread.currentThread().getName() + "阶段2的任务开始了");
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + "阶段2的任务完成了");
// 到达并在此等待其他线程到达
phaser.arriveAndAwaitAdvance();
System.out.println(Thread.currentThread().getName() + "阶段3的任务开始了");
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + "阶段3的任务完成了");
// 到达并在此等待其他线程到达
phaser.arriveAndAwaitAdvance();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class PhaserDemo {
public static void main(String[] args) {
Phaser phaser = new MyPhaser(3);
for (int i = 1; i <= 3; i++) {
new Task(phaser).start();
}
}
}
五、交换器 Exchanger
交换器用于两个线程之间交换信息。当在一个线程中调用Exchanger的exchange方法后,会等待对方线程调用Exchanger的exchange方法,对方线程调用exchange方法后,双方线程交换参数,双方线程继续执行。
常用方法
// 构造方法
public Exchanger()
// 调用此方法后,当前线程会等待对方线程调用exchange方法,对方线程调用exchange方法后,双方线程交换参数,双方线程继续执行。
public V exchange(V x)
// exchange(V x)的超时重载,如果在timeout时间内对方线程没有调用exchange方法,则会抛出TimeoutException。
public V exchange(V x, long timeout, TimeUnit unit)
使用示例
class ExchangerThread extends Thread {
private Exchanger exchanger;
private String name;
public ExchangerThread(Exchanger exchanger, String name) {
this.exchanger = exchanger;
this.name = name;
setName(name);
}
public void run() {
try {
System.out.println(Thread.currentThread().getName() + ": " + exchanger.exchange(name));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ExchangerDemo {
public static void main(String[] args) {
Exchanger exchanger = new Exchanger();
Thread xxx = new ExchangerThread(exchanger, "xxx");
Thread yyy = new ExchangerThread(exchanger, "yyy");
xxx.start();
yyy.start();
}
}