JAVA為我們提供了一些效果非常不錯的并發工具類,這里主要介紹一下如下幾個工具類的使用,并不會去深究實現原理(其實原理都是通過自旋CAS,CAS對應的處理器原子操作指令是CMPXCHG)。
Semaphore
CountDownLatch
CyclicBarrier
Executors
一、Semaphore
1、作用
設定信號量,只有獲得信號量的線程才能夠往后執行業務邏輯,沒有獲得信號量的線程只能阻塞等待喚醒重新嘗試獲得信號量,可用于服務限流。
2、代碼示例
/**
* Semaphore作用:設定信號量,只有獲得信號量的線程才能夠往后執行業務邏輯,
* 沒有獲得信號量的線程只能阻塞等待喚醒重新嘗試獲得信號量,可用于服務限流
* @author 愛吃魚的烏賊
*/
public class SemaphoreTest {
public static void main(String[] args) {
//設定信號量,只允許2個線程同時處理
Semaphore semaphore = new Semaphore(2);
for(int i=0;i<10;i++) {
new Thread(new Runnable() {
public void run() {
try {
//獲取信號量
semaphore.acquire();
System.out.println("Thread:"+Thread.currentThread().getId()+"獲得信號量"+new Date());
Thread.sleep(5000);
System.out.println("Thread:"+Thread.currentThread().getId()+"釋放信號量"+new Date());
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
}
二、CountDownLatch
1、作用
能夠使一個線程等待其他線程完成各自的工作后再執行
2、代碼示例
/**
* CountDownLatch 能夠使一個線程等待其他線程完成各自的工作后再執行
* @author 愛吃魚的烏賊
*/
public class CountDownLatchTest {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(2);
new Task1(countDownLatch).start();
new Task2(countDownLatch).start();
countDownLatch.await();
System.out.println("起鍋燒油");
}
public static class Task1 extends Thread{
CountDownLatch countDownLatch;
public Task1(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
System.out.println("正在洗菜中....");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("菜已洗好!");
countDownLatch.countDown();
}
}
public static class Task2 extends Thread{
CountDownLatch countDownLatch;
public Task2(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
System.out.println("正在煮飯中....");
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("飯已煮好!");
countDownLatch.countDown();
}
}
}
三、CyclicBarrier
1、作用
柵欄屏障,讓一組線程到達一個屏障(也可以叫同步點)時被阻塞,直到最后一個線程到達屏障時,屏障才會開門,所有被屏障攔截的線程才會繼續運行。
2、代碼示例
/**
* CyclicBarrier 柵欄屏障,讓一組線程到達一個屏障(也可以叫同步點)時被阻塞,
* 直到最后一個線程到達屏障時,屏障才會開門,所有被屏障攔截的線程才會繼續運行。
* @author 愛吃魚的烏賊
*/
public class CyclicBarrierTest {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier =new CyclicBarrier(10);
for(int i=0;i<10;i++) {
new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(new Random().nextInt(1000)+1000);
System.out.println("Thread:"+Thread.currentThread().getId()+"已經準備好");
cyclicBarrier.await();
System.out.println("Thread:"+Thread.currentThread().getId()+"開始出發");
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
}
}
四、Executors
1、作用
主要用來創建線程池,代理了線程池的創建,使得你的創建入口參數變得簡單
- newCachedThreadPool創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程。
- newFixedThreadPool 創建一個定長線程池,可控制線程最大并發數,超出的線程會在隊列中等待。
- newScheduledThreadPool 創建一個定長線程池,支持定時及周期性任務執行。
- newSingleThreadExecutor 創建一個單線程化的線程池,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO,LIFO, 優先級)執行。
2、代碼示例
/**
* Executors:主要用來創建線程池,代理了線程池的創建,使得你的創建入口參數變得簡單
* @author 愛吃魚的烏賊
*/
public class ExecutorsTest {
public static void main(String[] args) {
// 1、newCachedThreadPool創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程。
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
// 這里會發現結果有重復的線程ID
System.out.println("Thread:" + Thread.currentThread().getId() + ";newCachedThreadPool");
}
});
}
// 2、newFixedThreadPool 創建一個定長線程池,可控制線程最大并發數,超出的線程會在隊列中等待。
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(2);
for (int i = 0; i < 10; i++) {
newFixedThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 這里會發現兩個兩個執行完后才繼續執行,并且線程ID不變
System.out.println("Thread:" + Thread.currentThread().getId() + ";newFixedThreadPool");
}
});
}
// 3、newScheduledThreadPool 創建一個定長線程池,支持定時及周期性任務執行。
ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(2);
// 延遲3秒鐘后執行任務
newScheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
System.out.println("運行時間: " + new Date());
}
}, 3, TimeUnit.SECONDS);
// 延遲1秒鐘后每隔3秒執行一次任務
newScheduledThreadPool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("運行時間: " + new Date());
}
}, 1, 3, TimeUnit.SECONDS);
// 4、newSingleThreadExecutor 創建一個單線程化的線程池,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行。
ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
newSingleThreadExecutor.execute(new Runnable() {
@Override
public void run() {
// 這里會發現結果有重復的線程ID
System.out.println("Thread:" + Thread.currentThread().getId() + ";newSingleThreadExecutor");
}
});
}
}
}