文章目录
Java中的并发工具类
CountDownLatch(减法计数器,由一个同步队列实现)
概述
通过构造方法传入的参数作为计数器,当调用countDown()方法时,计数器的数值就会减一,CountDownLatch的await()方法就会阻塞当前线程,直到计数器变成0。如果某个线程解析的比较慢,我们可以使用**await(long time, TimeUtil unit)**这个方法等待特定的时间后,就不再阻塞当前线程。
初始化
public CountDownLatch(int count) {
//count必须大于等于0
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
/**
* 通过一个静态内部类(同步队列)实现
*/
private static final class Sync extends AbstractQueuedSynchronizer {
Sync(int count) {
setState(count);
}
}
private final Sync sync;
countDown()方法
public void countDown() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
//调用同步队列中的方法,共享式的释放同步状态
doReleaseShared();
return true;
}
return false;
}
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
//得到当前计数器的值
int c = getState();
if (c == 0)
return false;
//得到下一个计数器的值
int nextc = c-1;
//尝试将下一个计数器的值与第一个值交换
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
await()方法
public void await() throws InterruptedException {
//共享式的获取同步状态,如果当前线程未获取同步状态,就进入同步队列中等待,可中断。
sync.acquireSharedInterruptibly(1);
}
/**
* 计数器初始化
*/
/**
* countDown()方法,每次减一个。
*/
public void countDown() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
//得到当前计数器的值
int c = getState();
if (c == 0)
return false;
//得到下一个计数器的值
int nextc = c-1;
//尝试将下一个计数器的值与第一个值交换
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
使用
public class CountDownLatchTest {
static CountDownLatch c = new CountDownLatch(4);
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(3);
while (c.getCount() != 0){
c.countDown();
System.out.println(Thread.currentThread().getName()+":"+c.getCount());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
System.out.println("开始阻塞......");
c.await();
System.out.println("阻塞结束......");
}
}
开始阻塞......
A:3
A:2
A:1
A:0
阻塞结束......
注意
CountDownLatch的参数必须大于等于0。CountDownLatch不可能重新初始化或者修改CountDownLatch对象的内部计数器的值。
CyclicBarrier(同步屏障,通过两个整型实现)
概述
让一组线程到达一个屏障(同步点)时被阻塞,直到最后一个线程到达屏障时,所有阻塞的线程才能运行。通过await()方法设置同步点。
初始化
/**
*@param1 parties 同步点数量
*@param2 barrierAction 到达同步点优先运行的线程
*/
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
await()方法
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
/**
* ......省略
*/
//获取同步点-1后的数量
int index = --count;
//如果最后一个同步点已到达
if (index == 0) { // tripped
boolean ranAction = false;
try {
//运行第二个参数所传入的线程
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
//唤醒其他线程
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
// loop until tripped, broken, interrupted, or timed out
for (;;) {
/**
* ......省略计时等待代码。
*/
}
} finally {
lock.unlock();
}
}
reset()方法,重置计数器
public void reset() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
//给count重新赋值
breakBarrier(); // break the current generation
//唤醒所有线程
nextGeneration(); // start a new generation
} finally {
lock.unlock();
}
}
使用
public class CyclicBarrierTest {
static CyclicBarrier cyclicBarrier = new CyclicBarrier(2, new C());
public static void main(String[] args) {
new Thread(()->{
try {
cyclicBarrier.await();
}catch (Exception e){
e.printStackTrace();
}
System.out.println(""+Thread.currentThread().getName());
},"A").start();
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(2);
cyclicBarrier.await();
}catch (Exception e){
e.printStackTrace();
}
System.out.println(""+Thread.currentThread().getName());
},"B").start();
}
static class C implements Runnable{
@Override
public void run() {
System.out.println("C");
}
}
}
//打印结果
C
B
A
注意
CycliBarrier的计数器可以通过reset()方法重置,能处理更为复杂的业务场景。
Semaphore(信号量,同步队列实现)
概述
用来控制同时访问特定致员的线程数量。当当前访问的线程达到最大访问量时,阻塞其他线程。
初始化
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
acquire()方法
public void acquire() throws InterruptedException {
//可中断的共享获取同步状态,如果未获取,进入等待队列
sync.acquireSharedInterruptibly(1);
}
release
public void release() {
//共享释放同步状态
sync.releaseShared(1);
}
使用
public class SemaphoreTest {
static Semaphore semaphore = new Semaphore(2);
static ExecutorService service = Executors.newFixedThreadPool(6);
public static void main(String[] args) {
for (int i = 0; i < 6; i++) {
service.execute(() -> {
try {
semaphore.acquire();
TimeUnit.SECONDS.sleep(1);
System.out.println("当前时间"+new Date().getSeconds()+ "(s)" +":save data");
semaphore.release();
}catch (Exception e){
e.printStackTrace();
}
});
}
service.shutdown();
}
}
//打印结果
当前时间8(s):save data
当前时间8(s):save data
当前时间9(s):save data
当前时间9(s):save data
当前时间10(s):save data
当前时间10(s):save data
Exchanger(交换者,通过ThreadLocal实现)
概述
用于进行线程间的数据交换。它提供一个**同步点**,在这个同步点两个线程可以交换彼此的数据。线程可以通过exchange()方法到达同步点,第一个线程到达之后,会一直等待第二个线程到达,然后交换数据。
使用
public class ExchangerTest {
static Exchanger<String> exchanger = new Exchanger<>();
public static void main(String[] args) {
new Thread(()->{
String msg = "你好!很高兴见到你。";
try {
msg = exchanger.exchange(msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+msg);
},"A").start();
new Thread(()->{
try {
String s = exchanger.exchange("");
System.out.println(Thread.currentThread().getName()+":"+s);
} catch (InterruptedException e) {
e.printStackTrace();
}
},"B").start();
}
}
//打印结果
A:
B:你好!很高兴见到你。
本文深入介绍了Java中的四个并发工具类:CountDownLatch、CyclicBarrier、Semaphore和Exchanger。包括它们的原理、使用方法及示例代码,帮助读者理解如何有效利用这些工具来控制多线程间的同步。

2469

被折叠的 条评论
为什么被折叠?



