1.CountDownLatch
-
CountDownLatch允许一个或多个线程等待其他线程完成操作
-
join方法:
- join用于让当前执行线程等待join线程执行结束,原理是不停检查join线程是否存活,如果join线程存活则让当前线程永远等待
while (isAlive()) { wait(0); }
- 待join线程中止后,线程的
this.notifyAll()
方法会被调用(该方法是在JVM里实现的,JDK里看不到)
-
CountDownLatch的使用:
static CountDownLatch c = new CountDownLatch(2); // 设置计数器的值为2
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(1);
c.countDown(); // 计数器减1
System.out.println(2);
c.countDown(); // 计数器减1
}
}).start();
c.await(); // 阻塞主线程,直到计数器的值为0
System.out.println("3");
}
2.CyclicBarrier
-
让一组线程到达一个屏障(也叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行(所有线程等待完成,然后一起做事情)
-
每个线程调用await方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞
static CyclicBarrier c = new CyclicBarrier(2);
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
try {
---业务代码-----
// 插入一个屏障
c.await(); // 等另一个线程准备好再继续往下执行
} catch (Exception e) {
}
System.out.println(1);
}
}).start();
try {
---业务代码-----
// 插入一个屏障
c.await(); // 等另一个线程准备好再继续往下执行(这里是主线程等待另一个线程)
} catch (Exception e) {
}
System.out.println(2);
}
CyclicBarrier(int parties,Runnable barrierAction)
用于在线程到达屏障时,优先执行barrierAction
,方便处理更复杂的业务场景:
// 等代码中的第一个线程和线程A都执行完之后,才会继续执行主线程(输出3 1 2)
static CyclicBarrier c = new CyclicBarrier(2, new A());
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
try {
c.await(); // 插入一个屏障
} catch (Exception e) {
}
System.out.println(1);
}
}).start();
try {
c.await(); // 插入一个屏障
} catch (Exception e) {
}
System.out.println(2);
}
static class A implements Runnable {
@Override
public void run() {
System.out.println(3);
}
}
- CyclicBarrier和CountDownLatch的区别:
- CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以使用reset()方法重置(所以能处理更为复杂的业务场景)
- CountDownLatch是一/多个线程(老师)等待另外N个线程完成某个事情之后才能执行(N个学生做完了试卷就可走,不用等待其他的学生完成);CyclicBarrier是N个线程相互等待,任何一个线程完成之前,所有的线程都必须等待(一个游戏玩家加载到100%还不可以开局,须要等到其他N-1的游戏玩家都加载到100%才可以开局)
3.Semaphore
-
Semaphore(信号量)用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源
-
用于做流量控制,特别是公用资源有限的应用场景
private static final int THREAD_COUNT = 30;
private static ExecutorService threadPool = Executors.newFixedThreadPool(THREAD_COUNT);
private static Semaphore s = new Semaphore(10);
public static void main(String[] args) {
// 虽然有30个线程在执行,但是只允许10个并发执行
for (int i = 0; i< THREAD_COUNT; i++) {
threadPool.execute(new Runnable() {
@Override
public void run() {
try {
s.acquire(); // 获取许可证
System.out.println("save data");
s.release(); // 归还许可证
} catch (InterruptedException e) {
}
}
});
}
threadPool.shutdown();
}
4.Exchanger
-
Exchanger(交换者)是用于线程间协作的工具类(进行线程间的数据交换)
-
提供一个同步点,在这个同步点中两个线程可以交换彼此的数据
-
如果第一个线程先执行exchange方法,它会一直等待第二个线程也执行exchange方法,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产出来的数据传递给对方
private static final Exchanger<String> exgr = new Exchanger<>();
private static ExecutorService threadPool = Executors.newFixedThreadPool(2);
public static void main(String[] args) {
threadPool.execute(new Runnable() {
@Override
public void run() {
try {
String A = "银行流水1"; // A录入银行流水数据
exgr.exchange(A); // A表示需要和另个线程传递的数据
} catch (InterruptedException e) {
}
}
});
threadPool.execute(new Runnable() {
@Override
public void run() {
try {
String B = "银行流水2"; // B录入银行流水数据
String A = exgr.exchange(B); // B表示需要和另个线程传递的数据,A表示从另个线程提供的数据
System.out.println("A和B数据是否一致:" + A.equals(B) + ",A录入的是:" + A + ",B录入是:" + B);
} catch (InterruptedException e) {
}
}
});
threadPool.shutdown();
}