前言
jdk1.5并发库新增同步工具类,包括闭锁,信号量,循环栅栏。其本质不过是声明一个int类型volatile的变量state,来记录状态,也就是个数,通过不断的循环读取这个值,和同步的减小这个值,达到0,以达到阻塞线程和满足条件时放行的目的。
闭锁CountDownLatch
功能说明
一个例子
public class CountDownTest {
private CountDownLatch countDownLatch = new CountDownLatch(3);
public static void main(String[] args) throws InterruptedException {
CountDownTest countDownTest = new CountDownTest();
countDownTest.test();
System.out.println("-----验证不会再改变状态,这扇门将永远保持打开-------");
countDownTest.test();
}
public void test() throws InterruptedException {
new Thread(() -> {
try {
Thread.sleep(1000);
System.out.println("A搞到粉条回来了");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
countDownLatch.countDown();
}
}).start();
new Thread(() -> {
try {
Thread.sleep(2000);
System.out.println("B搞到白菜回来了");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
countDownLatch.countDown();
}
}).start();
new Thread(() -> {
try {
Thread.sleep(1000);
System.out.println("C搞到猪肉回来了");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
countDownLatch.countDown();
}
}).start();
System.out.println("我先开始烧火了。");
countDownLatch.await();
System.out.println("材料都到齐了,开始做。");
}
}
我先开始烧火了。
A搞到粉条回来了
C搞到猪肉回来了
B搞到白菜回来了
材料都到齐了,开始做。
-----验证不会再改变状态,这扇门将永远保持打开-------
我先开始烧火了。
材料都到齐了,开始做。
A搞到粉条回来了
C搞到猪肉回来了
B搞到白菜回来了
源码浅析
volatile变量保持可见性,用来记录当前状态。
/**
* The synchronization state.
*/
private volatile int state;
在构造方法中,实例化内部类Sync,设置state为count个。
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
protected final void setState(int newState) {
state = newState;
}
countDown()方法就是将state同步的减1。
public void countDown() {
sync.releaseShared(1);
}
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()方法是一个死循环,阻塞当前进程,不断的尝试获取state的数量,如果state为0后就放行。
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
for (;;) {}
}
信号量Semaphore
功能说明
一个例子
public class SemaphoreTest {
private static final int MAX_SEATS = 10;
private static final int PEOPLES = 15;
private static Semaphore semaphore = new Semaphore(MAX_SEATS);
ExecutorService executorService = Executors.newFixedThreadPool(20);
public static void main(String[] args) {
new SemaphoreTest().test();
}
public void test() {
for (int i = 0; i < PEOPLES; i++) {
executorService.execute(() -> {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "获得许可进入");
Thread.sleep(2000);
semaphore.release();
System.out.println(Thread.currentThread().getName() + "吃完走了。");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executorService.shutdown();
}
}
pool-1-thread-1获得许可进入
pool-1-thread-2获得许可进入
pool-1-thread-3获得许可进入
pool-1-thread-4获得许可进入
pool-1-thread-5获得许可进入
pool-1-thread-6获得许可进入
pool-1-thread-7获得许可进入
pool-1-thread-8获得许可进入
pool-1-thread-9获得许可进入
pool-1-thread-10获得许可进入
pool-1-thread-1吃完走了。
pool-1-thread-11获得许可进入
pool-1-thread-2吃完走了。
pool-1-thread-12获得许可进入
pool-1-thread-3吃完走了。
pool-1-thread-4吃完走了。
pool-1-thread-14获得许可进入
pool-1-thread-13获得许可进入
pool-1-thread-5吃完走了。
pool-1-thread-15获得许可进入
pool-1-thread-6吃完走了。
pool-1-thread-7吃完走了。
pool-1-thread-8吃完走了。
pool-1-thread-9吃完走了。
pool-1-thread-10吃完走了。
pool-1-thread-11吃完走了。
pool-1-thread-12吃完走了。
pool-1-thread-13吃完走了。
pool-1-thread-14吃完走了。
pool-1-thread-15吃完走了。
循环栅栏CyclicBarrier
功能说明
一个例子
public class CyclicBarrierTest {
//转载,可能源自张孝祥老师
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
// 创建CyclicBarrier对象并设置3个公共屏障点
final CyclicBarrier cb = new CyclicBarrier(3);
System.out.println("创建子线程开始");
for (int i = 0; i < 3; i++) {
Runnable runnable = () -> {
try {
Thread.sleep((long) (Math.random() * 10000));
System.out.println(
"线程" + Thread.currentThread().getName() + "即将到达集合地点1,当前已有" + (cb.getNumberWaiting() + 1)
+ "个已到达" + (cb.getNumberWaiting() == 2 ? "都到齐了,继续走啊" : "正在等候"));
try {
cb.await();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
Thread.sleep((long) (Math.random() * 10000));
System.out.println(
"线程" + Thread.currentThread().getName() + "即将到达集合地点2,当前已有" + (cb.getNumberWaiting() + 1)
+ "个已到达" + (cb.getNumberWaiting() == 2 ? "都到齐了,继续走啊" : "正在等候"));
try {
cb.await();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
Thread.sleep((long) (Math.random() * 10000));
System.out.println(
"线程" + Thread.currentThread().getName() + "即将到达集合地点3,当前已有" + (cb.getNumberWaiting() + 1)
+ "个已到达" + (cb.getNumberWaiting() == 2 ? "都到齐了,继续走啊" : "正在等候"));
try {
cb.await();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
};
service.execute(runnable);
System.out.println("子线程 "+ i + "提交到线程池中");
}
System.out.println("所有子线程提交完毕,线程池关闭,但线程池关闭的真实时间为所有子线程都执行完毕后");
service.shutdown();//线程池的状态则立刻变成SHUTDOWN状态,以后不能再往线程池中添加任何任务,否则将会抛出RejectedExecutionException异常。但是,此时线程池不会立刻退出,直到添加到线程池中的任务都已经处理完成,才会退出。 与它相似的还有一个shutdownNow(),它通过调用Thread.interrupt来实现线程的立即退出。
}
}
创建子线程开始
子线程 0提交到线程池中
子线程 1提交到线程池中
子线程 2提交到线程池中
所有子线程提交完毕,线程池关闭,但线程池关闭的真实时间为所有子线程都执行完毕后
线程pool-1-thread-3即将到达集合地点1,当前已有1个已到达正在等候
线程pool-1-thread-1即将到达集合地点1,当前已有2个已到达正在等候
线程pool-1-thread-2即将到达集合地点1,当前已有3个已到达都到齐了,继续走啊
线程pool-1-thread-3即将到达集合地点2,当前已有1个已到达正在等候
线程pool-1-thread-1即将到达集合地点2,当前已有2个已到达正在等候
线程pool-1-thread-2即将到达集合地点2,当前已有3个已到达都到齐了,继续走啊
线程pool-1-thread-3即将到达集合地点3,当前已有1个已到达正在等候
线程pool-1-thread-2即将到达集合地点3,当前已有2个已到达正在等候
线程pool-1-thread-1即将到达集合地点3,当前已有3个已到达都到齐了,继续走啊
来源:oschina
链接:https://my.oschina.net/wecanweup/blog/4340335