读《Java并发编程的艺术》笔记。
一、CountDownLatch
等待多线程完成的
CountDownLatch
,允许一个或多个线程等待其他线程完成操作。底层依赖AQS
实现。
例子:
import java.util.concurrent.CountDownLatch;
/**
* {@link CountDownLatch} 测试类
* <p>
*
* @author hyl
* @version v1.0: CountDownLatchTest.java, v 0.1 2020/10/6 8:33 $
*/
public class CountDownLatchTest {
static final int THREAD_COUNT = 4;
static CountDownLatch start = new CountDownLatch(1);
static CountDownLatch end = new CountDownLatch(THREAD_COUNT);
public static void main(String[] args) throws InterruptedException {
Runnable runnable = () -> {
System.out.println(Thread.currentThread()
.getName() + ": start");
try {
start.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread()
.getName() + ": end");
end.countDown();
};
for (int i = 0; i < THREAD_COUNT; i++) {
Thread thread = new Thread(runnable, "run-thread-" + i);
thread.start();
}
start.countDown();
end.await();
System.out.println(Thread.currentThread()
.getName() + ": final");
}
}
执行结果:
run-thread-1: start
run-thread-3: start
run-thread-0: start
run-thread-2: start
run-thread-2: end
run-thread-3: end
run-thread-1: end
run-thread-0: end
main: final
二、CyclicBarrier
同步屏障
CyclicBarrier
,字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一个组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏蔽才会开门,所有被屏障拦截的线程才会继续运行。底层依赖ReentrantLock
实现。
例子:
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* {@link CyclicBarrier} 测试类
* <p>
*
* @author hyl
* @version v1.0: CyclicBarrierTest.java, v 0.1 2020/10/6 6:27 $
*/
public class CyclicBarrierTest {
static CyclicBarrier c = new CyclicBarrier(2, () -> System.out.println("start:"));
public static void main(String[] args) {
new Thread(() -> {
try {
c.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(1);
}).start();
try {
c.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(2);
}
}
执行结果:
start:
1
2
CountDownLatch
和CyclicBarrier
的区别
CountDownLatch
底层依赖AQS
实现。CyclicBarrier
底层依赖ReentrantLock
实现。CountDownLatch
的计算器只能使用一次,而CyclicBarrier
的计数器可以使用reset()
方法重置。CyclicBarrier
还提供其他有用的方法,getNumberWaiting()
方法可以获得CyclicBarrier
阻塞的线程数量,isBroken()
方法用来了解阻塞的线程是否被中断。
三、Semaphore
Semaphore
(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。底层依赖AQS
实现,存在公平和非公平两种模式,类似ReentrantLock
。
例子:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
/**
* {@link java.util.concurrent.Semaphore }测试类
* <p>
*
* @author hyl
* @version v1.0: SemaphoreTest.java, v 0.1 2020/10/6 7:36 $
*/
public class SemaphoreTest {
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) {
for (int i = 0; i < THREAD_COUNT; i++) {
threadPool.execute(() -> {
try {
s.acquire();
// 随机产生休眠时间
int time = new Object().hashCode() >> 28;
time = time == 0 ? 1 : time;
System.out.println(Thread.currentThread()
.getName() + ": save data >> " + time);
try {
Thread.sleep(time * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
s.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
threadPool.shutdown();
}
}
执行结果:
pool-1-thread-2: save data >> 6
pool-1-thread-1: save data >> 1
pool-1-thread-3: save data >> 2
pool-1-thread-5: save data >> 6
pool-1-thread-4: save data >> 2
pool-1-thread-7: save data >> 1
pool-1-thread-6: save data >> 6
pool-1-thread-9: save data >> 5
pool-1-thread-8: save data >> 1
pool-1-thread-10: save data >> 1
pool-1-thread-11: save data >> 2
pool-1-thread-13: save data >> 6
pool-1-thread-14: save data >> 5
pool-1-thread-12: save data >> 2
pool-1-thread-15: save data >> 4
pool-1-thread-16: save data >> 5
pool-1-thread-17: save data >> 1
pool-1-thread-18: save data >> 7
pool-1-thread-19: save data >> 3
pool-1-thread-20: save data >> 1
pool-1-thread-22: save data >> 4
pool-1-thread-21: save data >> 1
pool-1-thread-23: save data >> 4
pool-1-thread-26: save data >> 7
pool-1-thread-24: save data >> 4
pool-1-thread-25: save data >> 3
pool-1-thread-27: save data >> 6
pool-1-thread-29: save data >> 6
pool-1-thread-28: save data >> 6
pool-1-thread-30: save data >> 3
四、Exchanger
线程间交互数据的
Exchanger
(交换者),一个用于线程间协作的工具类。Exchanger
用于进行线程间的数据交换。它提供了一个同步点,在这个同步点,两个线程可以交换彼此的数据。这两个线程通过exchange()
方法交换数据,如果第一个线程先执行exchange()
方法,它会一直等待第二线程也执行exchange()
方法,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程产生出来的数据传递给对方。
可用于遗传算法,用于校对工作。
底层依赖
ThreadLocal
和Unsafe
实现。
例子:
import java.util.concurrent.Exchanger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* {@link java.util.concurrent.Exchanger} 测试类
* <p>
*
* @author hyl
* @version v1.0: ExchangerTest.java, v 0.1 2020/10/6 8:06 $
*/
public class ExchangerTest {
private static final Exchanger<String> exgr = new Exchanger<>();
private static ExecutorService threadPool = Executors.newFixedThreadPool(2);
public static void main(String[] args) {
threadPool.execute(() -> {
String A = "银行流水A";
try {
String B = exgr.exchange(A);
System.out.println(Thread.currentThread()
.getName() + ":A拿到了B录入的:" + B);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
threadPool.execute(() -> {
String B = "银行流水B";
try {
String A = exgr.exchange(B);
System.out.println(Thread.currentThread()
.getName() + ":A和B数据是否一致:" + A.equals(B) + ",A录入的是:" + A + ",B录入的是:" + B);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
threadPool.shutdown();
}
}
执行结果:
pool-1-thread-1:A拿到了B录入的:银行流水B
pool-1-thread-2:A和B数据是否一致:false,A录入的是:银行流水A,B录入的是:银行流水B