JUC从JDK1.5开始,就是这个包里有很多工具类,这次介绍这三个大头。
CountDownLatch
package com.juc;
import java.util.concurrent.*;
public class CountDownLatchTest {
public static void main(String[] args) {
//主线程 加载游戏资源
CountDownLatch count = new CountDownLatch(10);
//十个子线程代表十个玩家正在加载游戏
ExecutorService exec = Executors.newFixedThreadPool(11);
Runnable game = new Runnable() {
@Override
public void run() {
try {
count.await();
System.out.println("全部加载完毕!进入英雄联盟!");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
exec.shutdown();
}
}
};
exec.execute(game);
for (int i = 0; i < 10; i++) {
exec.execute(new Player("玩家小"+i,count));
}
}
}
class Player implements Runnable{
private String loler;
private CountDownLatch count;
public Player(String loler,CountDownLatch count){
this.loler = loler;
this.count = count;
}
public String getLoler() {
return loler;
}
public void setLoler(String loler) {
this.loler = loler;
}
@Override
public void run() {
//加载完成一个玩家,则计数器减一
count.countDown();
System.out.println(this.loler+"加载lol完毕!");
}
}
结果如下~
重要方法
- 构造函数 传入count数量
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
- await()方法,等待CountDownLatch 内部维护的count为0时执行余下逻辑
- countDown() 方法,将CountDownLatch内部的count数量-1
- 内部类重写了tryAcquireShared方法来判断是否释放当前线程
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -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;
}
重要变量
- 静态内部类 private static final class Sync extends AbstractQueuedSynchronizer
- Sync继承自 的AbstractQueuedSynchronizer的变量 private volatile int state;
原理
- 构造函数将count 传入静态内部类Sync的state,根据监控state是否为0来阻塞await的线程
Cyclicbarrier
循环栅栏,
构造函数
也是用到了方法的重载
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
public CyclicBarrier(int parties) {
this(parties, null);
}
重要方法
- await 没错还是他,阻塞线程,等待所有 parties个线程都ok了才往下走
举个例子
线程await后互相等待
package com.juc;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class CyclicBarrierTest {
public static void main(String\[\] args) {
//主线程 加载游戏资源
CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
//十个子线程代表十个玩家正在加载游戏
ExecutorService exec = new ThreadPoolExecutor(10, 10,
0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
for (int i = 0; i < 10; i++) {
exec.execute(new Player1("玩家小" \+ i, cyclicBarrier));
}
}
}
class Player1 implements Runnable {
private String loler;
private CyclicBarrier cyclicBarrier;
public Player1(String loler, CyclicBarrier cyclicBarrier) {
this.loler = loler;
this.cyclicBarrier = cyclicBarrier;
}
public String getLoler() {
return loler;
}
public void setLoler(String loler) {
this.loler = loler;
}
@Override
public void run() {
//加载完成一个玩家,则计数器减一
System.out.println(this.loler \+ "开始加载游戏!");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(this.loler \+ "加载lol完毕!");
}
}
结果就是这样
// 构造函数传入 一个Runnble的实现类,瞅瞅他会咋样
package com.juc;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class CyclicBarrierTest {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier1 = new CyclicBarrier(10, new Thread(()->{
System.out.println("大家都加载了啊,那就开始进入游戏把");
}));
//十个子线程代表十个玩家正在加载游戏
ExecutorService exec1 = new ThreadPoolExecutor(10, 10,
0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
for (int i = 0; i < 10; i++) {
exec1.execute(new Player1("玩家小" \+ i, cyclicBarrier1));
}
}
}
class Player1 implements Runnable {
private String loler;
private CyclicBarrier cyclicBarrier;
public Player1(String loler, CyclicBarrier cyclicBarrier) {
this.loler = loler;
this.cyclicBarrier = cyclicBarrier;
}
public String getLoler() {
return loler;
}
public void setLoler(String loler) {
this.loler = loler;
}
@Override
public void run() {
//加载完成一个玩家,则计数器减一
System.out.println(this.loler \+ "开始加载游戏!");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
// System.out.println(this.loler + "加载lol完毕!");
System.out.println(this.loler + "进入游戏!");
}
}
从结果可以看出,所有线程都await()准备就绪后,开始跑传入构造函数的线程。再开始所有线程await后的逻辑。
有reset方法,让内部的count重置,重新开始,可以复用Cyclicbarrier对象,对比DownCountLatch。如果DownCountLatch在执行countDown()后,调用await也可以达到Cyclicbarrier的await方法等待其余剩余线程的feel
reset方法使用案例
package com.juc;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class CyclicBarrierTest {
public static void main(String\[\] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(10, new Thread(() -> {
System.out.println("大家都加载了啊,那就开始进入游戏把");
}));
//十个子线程代表十个玩家正在加载游戏
ExecutorService exec1 = new ThreadPoolExecutor(10, 10,
0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
for (int i = 0; i < 10; i++) {
exec1.execute(new Player1("玩家小" \+ i, cyclicBarrier));
}
exec1.shutdown();
cyclicBarrier.reset();
ExecutorService exec = new ThreadPoolExecutor(10, 10,
0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
for (int i = 0; i < 10; i++) {
exec.execute(new Player1("第二次玩家小" \+ i, cyclicBarrier));
}
}
}
class Player1 implements Runnable {
private String loler;
private CyclicBarrier cyclicBarrier;
public Player1(String loler, CyclicBarrier cyclicBarrier) {
this.loler = loler;
this.cyclicBarrier = cyclicBarrier;
}
public String getLoler() {
return loler;
}
public void setLoler(String loler) {
this.loler = loler;
}
@Override
public void run() {
//加载完成一个玩家,则计数器减一
System.out.println(this.loler \+ "开始加载游戏!");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
if ("玩家小5".equals(loler)) {
cyclicBarrier.reset();
}
System.out.println(this.loler \+ "进入游戏!");
}
}
Semaphore
用处
只有在调用Semaphore对象的aquire()成功后才可以往下执行,完成后执行release()释放持有的信号量,下一个线程就可以马上获取这个空闲信号量进入执行。
白话文:有两个茅坑,有五个人,五个人是线程,要占用两个茅坑 方便,当茅坑被线程占用aquire时,其他线程只能等着,一个结束了,另一个再上。
构造函数
// 传入可持有的信号量,同一时刻,只有permits个线程能跑
public Semaphore(int permits){
sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
重要方法
// 获取信号量
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
// 释放获取的信号量
public void release() {
sync.releaseShared(1);
}
DEMO
package com.juc;
import java.util.concurrent.Semaphore;
public class SemaphoreTest {
static class MyThread extends Thread{
private String name1;
private Semaphore semaphore;
public String getName1() {
return name1;
}
public void setName1(String name1) {
this.name1 = name1;
}
public Semaphore getSemaphore() {
return semaphore;
}
public void setSemaphore(Semaphore semaphore) {
this.semaphore = semaphore;
}
public MyThread(String name1, Semaphore semaphore) {
this.name1 = name1;
this.semaphore = semaphore;
}
@Override
public void run() {
try {
semaphore.acquire();
System.out.println(this.name1 +";acquire占坑拉屎");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
semaphore.release();
System.out.println(this.name1 +";release拉完走人");
}
}
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(2);
for (int i = 0; i < 4; i++) {
new MyThread(""+i,semaphore).start();
}
}
}
那么结论很明显,同一时间只有两个线程在跑,其他线程要运行必须等其中一个线程release完毕才可
总结
无论从性能还是安全上考虑都是JUC并发包的信号同步类比较好,避免使用对象的wait(),notify()方法
来源:oschina
链接:https://my.oschina.net/xlpapapa/blog/3164054