线程中常用的一些同步工具类:
- 阻塞队列
- 信号量(Semaphore)
- 栅栏(Barrier)
- 闭锁(Latch)
Semaphore
注意点:
获得一项前,每个线程必须从信号量获取许可,从而保证可以使用该项。该线程结束后,将项返回到池中并将许可返回到该信号量,从而允许其他线程获取该项。注意,调用 acquire() 时无法保持同步锁,因为这会阻止将项返回到池中。信号量封装所需的同步,以限制对池的访问,这同维持该池本身一致性所需的同步是分开的。
示例
public class SemaphoreTest {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(4);
// 创建一个Semaphore信号量,并设置最大并发数为3
final Semaphore sp = new Semaphore(3);
// 创建10个任务,上面的缓存线程池就会创建10个对应的线程去执行
for (int index = 0; index < 10; index++) {
final int NO = index;
Runnable run = () -> {
try {
// 获取许可
sp.acquire();
System.out.println(Thread.currentThread().getName() + "获取许可" + "(" + NO + ")," + "剩余:" + sp.availablePermits());
Thread.sleep(10000);
// 释放许可
sp.release();
System.out.println(Thread.currentThread().getName() + "释放许可" + "(" + NO + ")," + "剩余:" + sp.availablePermits());
} catch (InterruptedException e) {
System.out.println("InterruptedException:" + e);
}
};
service.execute(run);
}
// 关闭线程池
service.shutdown();
}
}
CountDownLatch
示例:
public class Test {
public static void main(String[] args) {
final CountDownLatch latch = new CountDownLatch(3);
// 继承Thread类
new Thread(){
@Override
public void run() {
try {
System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
Thread.sleep(3000);
System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}.start();
new Thread(){
@Override
public void run() {
try {
System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
Thread.sleep(3000);
System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}.start();
// 实现Runnable接口
((Runnable) () -> {
try {
System.out.println("Runnable");
Thread.sleep(3000);
System.out.println("Runnable执行完毕");
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).run();
try {
System.out.println("等待2个子线程执行完毕...");
latch.await();
System.out.println("2个子线程已经执行完毕");
System.out.println("继续执行主线程");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
CyclicBarrier
需要可重用的 CountDownLatch,考虑使用 CyclicBarrier。
public class Athlete implements Runnable {
private CyclicBarrier cyclicBarrier;
private String name;
public Athlete(CyclicBarrier cyclicBarrier, String name) {
this.cyclicBarrier = cyclicBarrier;
this.name = name;
}
@Override
public void run() {
System.out.println(name + ",就位");
try {
cyclicBarrier.await();
Random random = new Random();
double time = random.nextDouble() + 9;
Thread.sleep(1000L);
System.out.println(name + ":" + time);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
public class Race {
Executor executor = Executors.newFixedThreadPool(8);
private CyclicBarrier cyclicBarrier = new CyclicBarrier(8);
public void start() {
List<Athlete> athleteList = new ArrayList<>(16);
athleteList.add(new Athlete(cyclicBarrier, "博尔特"));
athleteList.add(new Athlete(cyclicBarrier, "鲍威尔"));
athleteList.add(new Athlete(cyclicBarrier, "盖伊"));
athleteList.add(new Athlete(cyclicBarrier, "布雷克"));
athleteList.add(new Athlete(cyclicBarrier, "加特林"));
athleteList.add(new Athlete(cyclicBarrier, "苏炳添"));
athleteList.add(new Athlete(cyclicBarrier, "路人甲"));
athleteList.add(new Athlete(cyclicBarrier, "路人乙"));
for (Athlete athlete : athleteList) {
executor.execute(athlete);
}
}
public static void main(String[] args) {
Race race = new Race();
race.start();
}
}