CountDownLatch
CountDownLatch是一個計數器閉鎖,通過它可以完成類似於阻塞當前線程的功能,即:一個線程或多個線程一直等待,直到其他線程執行的操作完成。當計數器值減至零時,所有因調用await()方法而處於等待狀態的線程就會繼續往下執行。這種現象只會出現一次,計數器不能被重置。
package com.xiaobu.JUC;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* @author xiaobu
* @version JDK1.8.0_171
* @date on 2019/9/20 11:27
* @description 考試場景 20個學生參加考試 一個人老師監考,只有最後一個學生交卷 老師纔算任務完成
*/
public class CountDownLatchDemo {
private static final int count = 20;
static class Teacher implements Runnable{
private CountDownLatch countDownLatch;
public Teacher(){}
public Teacher(CountDownLatch countDownLatch){
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
System.out.print("老師髮捲子\n");
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print("老師收卷子\n");
}
}
static class Student implements Runnable{
private CountDownLatch countDownLatch;
private int num;
public Student(){}
public Student(CountDownLatch countDownLatch,int num){
this.countDownLatch = countDownLatch;
this.num = num;
}
@Override
public void run() {
System.out.printf("學生(%d)寫卷子\n",num);
System.out.printf("學生(%d)交卷子\n",num);
countDownLatch.countDown();
}
}
public static void main(String[] args) {
CountDownLatch countDownLatch = new CountDownLatch(count);
Teacher teacher = new Teacher(countDownLatch);
Thread teacherThread = new Thread(teacher);
teacherThread.start();
try {
//爲了防止還沒髮捲子 學生就開始寫卷子了
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < count; i++) {
Student student = new Student(countDownLatch, i);
Thread studentThread = new Thread(student);
studentThread.start();
}
}
}
package com.xiaobu.note.JUC.CountDownLatch;
import java.util.concurrent.CountDownLatch;
/**
* @author xiaobu
* @version JDK1.8.0_171
* @date on 2019/2/26 16:34
* @description V1.0
*/
public class Boss implements Runnable {
private CountDownLatch downLatch;
public Boss(CountDownLatch downLatch){
this.downLatch = downLatch;
}
@Override
public void run() {
System.out.println("老闆正在等所有的工人幹完活......");
try {
this.downLatch.await();
} catch (InterruptedException e) {
}
System.out.println("工人活都幹完了,老闆開始檢查了!");
}
}
package com.xiaobu.note.JUC.CountDownLatch;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* @author xiaobu
* @version JDK1.8.0_171
* @date on 2019/2/26 16:33
* @description V1.0
*/
public class Worker implements Runnable{
private CountDownLatch downLatch;
private String name;
public Worker(CountDownLatch downLatch, String name){
this.downLatch = downLatch;
this.name = name;
}
@Override
public void run() {
this.doWork();
try{
TimeUnit.SECONDS.sleep(new Random().nextInt(10));
}catch(InterruptedException ie){
ie.printStackTrace();
}
System.out.println(this.name + "活幹完了!");
this.downLatch.countDown();
}
private void doWork(){
System.out.println(this.name + "正在幹活!");
}
}
package com.xiaobu.note.JUC.CountDownLatch;
import java.util.concurrent.*;
/**
* @author xiaobu
* @version JDK1.8.0_171
* @date on 2019/2/26 16:34
* @description V1.0
*/
public class CountDownLatchDemo {
public static void main(String[] args) {
//兩者等價但需要顯現的創建線程池這樣不會出現OOM的情況
ExecutorService e = Executors.newCachedThreadPool();
ExecutorService executor=new ThreadPoolExecutor(0,5,60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
CountDownLatch latch = new CountDownLatch(3);
Worker w1 = new Worker(latch,"張三");
Worker w2 = new Worker(latch,"李四");
Worker w3 = new Worker(latch,"王二");
Boss boss = new Boss(latch);
executor.execute(w3);
executor.execute(w2);
executor.execute(w1);
executor.execute(boss);
executor.shutdown();
}
}
CyclicBarrier
CyclicBarrier也是一個同步輔助類,它允許一組線程相互等待,直到到達某個公共屏障點(common barrier point)。通過它可以完成多個線程之間相互等待,只有當每個線程都準備就緒後,才能各自繼續往下執行後面的操作。
package com.xiaobu.JUC;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* @author xiaobu
* @version JDK1.8.0_171
* @date on 2019/9/20 11:56
* @description
* 最近景色宜人,公司組織去登山,大夥都來到了山腳下,登山過程自由進行。
*
* 但爲了在特定的地點拍集體照,規定1個小時後在半山腰集合,誰最後到的,要給大家表演一個節目。
*
* 然後繼續登山,在2個小時後,在山頂集合拍照,還是誰最後到的表演節目。
*
* 接着開始下山了,在2個小時後在山腳下集合,點名回家,最後到的照例表演節目。
*/
@Slf4j
public class CyclicBarrierDemo {
private static final int count = 5;
private static CyclicBarrier cyclicBarrier = new CyclicBarrier(count,new Singer());
public static void main(String[] args) {
for (int i = 0; i < count; i++) {
Staff staff = new Staff(cyclicBarrier, i);
Thread thread = new Thread(staff);
thread.start();
}
}
static class Singer implements Runnable{
@Override
public void run() {
System.out.println("爲大家表演節目");
}
}
static class Staff implements Runnable{
private int num;
private CyclicBarrier cyclicBarrier;
public Staff(){}
public Staff(CyclicBarrier cyclicBarrier,int num){
this.cyclicBarrier = cyclicBarrier;
this.num = num;
}
@Override
public void run() {
log.info("員工[{}]開始出發....",num);
log.info("員工[{}]到達目的地一....",num);
try {
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
log.info("員工[{}]再次出發....",num);
log.info("員工[{}]到達目的地二....",num);
try {
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
log.info("員工[{}]又一次出發....",num);
log.info("員工[{}]到達目的地三....",num);
try {
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
log.info("員工[{}]結束行程....",num);
}
}
}
Semaphore
Semaphore與CountDownLatch相似,不同的地方在於Semaphore的值被獲取到後是可以釋放的,並不像CountDownLatch那樣一直減到底。它也被更多地用來限制流量,類似閥門的 功能。如果限定某些資源最多有N個線程可以訪問,那麼超過N個主不允許再有線程來訪問,同時當現有線程結束後,就會釋放,然後允許新的線程進來。
void acquire() 從信號量獲取一個許可,如果無可用許可前 將一直阻塞等待,
void acquire(int permits) 獲取指定數目的許可,如果無可用許可前 也將會一直阻塞等待
boolean tryAcquire() 從信號量嘗試獲取一個許可,如果無可用許可,直接返回false,不會阻塞
boolean tryAcquire(int permits) 嘗試獲取指定數目的許可,如果無可用許可直接返回false,
boolean tryAcquire(int permits, long timeout, TimeUnit unit) 在指定的時間內嘗試從信號量中獲取許可,如果在指定的時間內獲取成功,返回true,否則返回false
void release() 釋放一個許可,別忘了在finally中使用,注意:多次調用該方法,會使信號量的許可數增加,達到動態擴展的效果,如:初始permits 爲1, 調用了兩次release,最大許可會改變爲2
int availablePermits() 獲取當前信號量可用的許可
package com.xiaobu.JUC;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
/**
* @author xiaobu
* @version JDK1.8.0_171
* @date on 2019/7/3 11:19
* @description
*/
@Slf4j
public class SemaphoreDemo2 {
private final static int threadCount = 5;
public static void main(String[] args) throws Exception {
ExecutorService exec = Executors.newCachedThreadPool();
// 每次最多三個線程獲取許可
final Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < threadCount; i++) {
final int threadNum = i;
exec.execute(() -> {
try {
semaphore.acquire(); // 獲取一個許可
test(threadNum);
//把release註釋掉可以看出只執行三個就不能通過了
semaphore.release(); // 釋放一個許可
System.out.println("semaphore.availablePermits() = " + semaphore.availablePermits());
} catch (Exception e) {
log.error("exception", e);
}
});
}
exec.shutdown();
}
private static void test(int threadNum) throws Exception {
log.info("{}", threadNum);
Thread.sleep(2000);
}
}
release()可以使信號量的許可數增加,達到動態擴展的效果
package com.xiaobu.JUC;
import java.util.concurrent.Semaphore;
/**
* @author xiaobu
* @version JDK1.8.0_171
* @date on 2019/9/27 10:18
* @description release()可以使信號量的許可數增加,達到動態擴展的效果
*/
public class SemaphoreDemo3 {
private static Semaphore semaphore = new Semaphore(1);
public static void main(String[] args) {
try {
System.out.println("獲取前有" + semaphore.availablePermits() + "個可用許可");
semaphore.acquire();
System.out.println("獲取後有" + semaphore.availablePermits() + "個可用許可");
for (int i = 0; i < 3; i++) {
semaphore.release();
System.out.println("釋放"+i+"個許可後,還有"+semaphore.availablePermits()+"個可用許可");
}
for (int i = 0; i < 3; i++) {
semaphore.acquire();
System.out.println("獲取第"+i+"個許可,還有"+semaphore.availablePermits()+"可用許可");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package com.xiaobu.learn.concurrent;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import java.util.concurrent.*;
/**
* @author xiaobu
* @version JDK1.8.0_171
* @date on 2019/7/3 11:19
* @description
*/
@Slf4j
public class SemaphoreDemo {
private final static int threadCount = 5;
public static void main(String[] args) throws Exception {
ThreadFactory threadFactory = new BasicThreadFactory.Builder().namingPattern("thread-pool-%d").daemon(true).build();
ThreadPoolExecutor executor = new ThreadPoolExecutor(0, 30, 60L, TimeUnit.SECONDS, new SynchronousQueue<>(), threadFactory);
final Semaphore semaphore = new Semaphore(2);
for (int i = 0; i < threadCount; i++) {
final int threadNum = i;
executor.execute(() -> {
try {
if (semaphore.tryAcquire(1,TimeUnit.MILLISECONDS)) { // 嘗試獲取一個許可
log.info("[{}]獲取到許可,threadNum:[{}]",Thread.currentThread().getName(),threadNum);
test(threadNum);
}else {
log.info("[{}]沒有獲得許可,threadNum:[{}]",Thread.currentThread().getName(),threadNum);
}
} catch (Exception e) {
log.error("exception", e);
}finally {
log.info(Thread.currentThread().getName()+"釋放前semaphore.availablePermits() = {}個" , semaphore.availablePermits());
semaphore.release(); // 釋放一個許可
log.info(Thread.currentThread().getName()+"釋放後semaphore.availablePermits() ={}個" , semaphore.availablePermits());
}
});
}
//確保所有的線程都執行完
TimeUnit.SECONDS.sleep(5);
executor.shutdown();
log.info("任務結束");
}
private static void test(int threadNum) throws Exception {
System.out.println("test 中的threadNum:"+threadNum);
Thread.sleep(1000);
}
}