這個是在學習工作中的一些總結,若有不對之處歡迎大家指出。侵刪!
需要源碼聯繫QQ:1352057131
得之在俄頃,積之在平日。
目錄
CyclicBarrier與CountDownLatch的區別
控制併發流程
控制併發流程就是讓線程之間相互配合以滿足業務需求,比如:線程1等待線程2 3 4執行完後再執行。
控制併發流程工具類概覽
CountDownLatch
作用
倒計時門閂 例如:等長途汽車,等到所有位置都有人了就發車
流程
倒數結束之前,該線程一直等待,直到倒數結束之後,該線程纔會執行
主要方法
CountDownLatch(int count):構造函數,參數count爲需要倒數的值
void await():調用該方法的線程會被掛起,直到count的值爲0纔會繼續執行
void countDown():將count的值減1,直到爲0時,等待的線程會被喚醒。
示例1
public class CountDownLatchTest01 {
//多等一:模擬五個檢查官檢查工作
private static CountDownLatch countDownLatch = new CountDownLatch(5);
public static void main(String[] args) {
//創建一個容量爲5的線程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
String name = "檢查者"+(i+1);
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println(name+"開始檢查");
try {
Thread.sleep(new Random().nextInt(1000)*10);
System.out.println(name+"檢查結束");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
countDownLatch.countDown();
}
}
};
executorService.submit(runnable);
}
try {
countDownLatch.await();
while (true){
if (!executorService.isTerminated()){
executorService.shutdown();
break;
}
}
System.out.println("所有人檢查結束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
示例2
public class CountDownLatchTest02 {
//一等多:模擬比賽發令槍
private static CountDownLatch countDownLatch = new CountDownLatch(1);
public static void main(String[] args) throws InterruptedException {
//創建一個容量爲5的線程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
String name = "選手"+(i+1);
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println(name+"等待");
try {
countDownLatch.await();
System.out.println(name+"開始跑步");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
executorService.submit(runnable);
}
//等待所有的線程都準備完畢
Thread.sleep(1000);
System.out.println("發令槍響.....");
countDownLatch.countDown();
while (true){
if (!executorService.isTerminated()){
executorService.shutdown();
break;
}
}
}
}
示例3
public class CountDownLatchTest03 {
//一等多、多等一的混合使用
private static CountDownLatch begin = new CountDownLatch(1);
private static CountDownLatch end = new CountDownLatch(5);
public static void main(String[] args) throws InterruptedException {
//創建一個容量爲5的線程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
String name = "選手"+(i+1);
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println(name+"等待");
try {
begin.await();
System.out.println(name+"開始跑步");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
end.countDown();
}
}
};
executorService.submit(runnable);
}
//等待所有的線程都準備完畢
Thread.sleep(1000);
System.out.println("發令槍響.....");
begin.countDown();
end.await();
while (true){
if (!executorService.isTerminated()){
executorService.shutdown();
break;
}
}
System.out.println("所有人跑步完畢");
}
}
【CountDownLatch是不能重用的,如果需要重新計數則考慮CyclicBarrier或者創建新的CountDownLatch】
Semaphore
作用
限制有限資源的使用;例如:一個服務的方法特別耗時(大量數據的處理、大量文件生成等),如果大量用戶請求過來,則導致該服務不可用,如果使用信號量,前面的請求則會拿到許可證,後面的請求將會被阻塞,這樣就保障了該服務不會同時服務特別多的用戶而導致服務不可用。
使用流程
初始化semaphore並指定許可證數量。
執行任務之前調用acquire()方法或者acquireUninterRuptibly()方法。
在任務結束後調用release()方法。
常用方法
Semaphore(int permits):實例化Semaphore並指定令牌數量爲permits。
Semaphore(int permits, boolean fair) :實例化Semaphore並指定令牌數量和指定是否公平;如果fair爲true,將會把等待的線程放入FIFO隊列裏面
void acquire():獲取一個令牌,可以響應中斷。
void acquireUninterruptibly():獲取一個令牌,不可以響應中斷
boolean tryAcquire():看看有沒有許可證,如果沒有就去做別的事兒,過一會再來拿。
boolean tryAcquire(long timeout, TimeUnit unit):與tryAcquire一樣,只是設置了一個超時時間
void release():釋放一個令牌。
void acquire(int permits):獲取permits個令牌。
boolean tryAcquire(int permits):看看有沒有permits個許可證,如果沒有就去做別的事兒,過一會再來拿。
boolean tryAcquire(int permits, long timeout, TimeUnit unit):與tryAcquire(int permits)一樣,只是設置了一個超時時間。
void release(int permits):釋放permits個令牌。
【每次獲得了令牌必須在finaly中釋放】
示例
public class SemaphoreTest {
//創建擁有4個許可證的信號量
static Semaphore semaphore = new Semaphore(4);
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
service.submit(new Task());
}
}
}
class Task implements Runnable{
@Override
public void run() {
try {
//拿到許可證
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"拿到許可證");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println(Thread.currentThread().getName()+"釋放許可證");
semaphore.release();
}
}
}
Condition接口(條件對象)
作用
當線程1需要等待某個條件時,它就會執行condition.await()方法,線程進入阻塞狀態;假如線程2去執行對應的條件,當這個條件達成時就去執行condition.signal()方法,這時JVM就會從阻塞中的線程中找到等待該condition的線程,這時線程1就會收到可執行信號,線程狀態就會變成Runable
常用方法
void await():等待,線程進入阻塞狀態
void signal():喚起一個正在等待的線程
void signalAll():喚起所有正在等待的線程
示例
public class ConditionTest {
private static ReentrantLock lock = new ReentrantLock();
private static Condition condition = lock.newCondition();
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lock();
Thread.sleep(1000);
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lock();
System.out.println("----------");
condition.await();
System.out.println("==========");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}).start();
}
}
示例2:消費者生產者模式
public class ConditionTest02 {
static ReentrantLock lock = new ReentrantLock();
static Condition consumerCondition = lock.newCondition();
static Condition producerCondition = lock.newCondition();
static ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(10);
public static void main(String[] args) {
Thread thread0 = new Thread(new Consumer());
Thread thread1 = new Thread(new Producer());
thread0.start();
thread1.start();
}
static class Consumer implements Runnable{//消費者
@Override
public void run() {
while (true){
Cons();
}
}
private void Cons(){
try {
lock.lock();
while (queue.size()==0){
System.out.println("隊列裏沒有數據,等待補充數據");
consumerCondition.await();
producerCondition.signalAll();
}
System.out.println("隊列裏有"+queue.size()+"條數據消費了1條數據還剩"+(queue.size()-1)+"條數據");
queue.poll();
producerCondition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
static class Producer implements Runnable{//生產者
@Override
public void run() {
while (true){
Prod();
}
}
private void Prod(){
try {
lock.lock();
int i = 0;
while (queue.size()==10){
System.out.println("隊列裏已滿");
producerCondition.await();
consumerCondition.signalAll();
}
System.out.println("隊列裏有"+queue.size()+"條數據補充了1條數據還剩"+(queue.size()+1)+"條數據");
queue.add("數據"+(i++));
consumerCondition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
注意點
Condition的用法與Object.wait/notify的用法幾乎一樣。
Await方法自動釋放持有的lock鎖,和Object.wait一樣不需要手動釋放鎖。
調用await方法的時候必須持有鎖,不然會拋出異常。
CyclicBarrier循環柵欄
作用
CyclicBarrier循環柵欄和CountDownLatch很類似,都能阻塞一組線程。
當有大量線程相互配合分別計算不同的任務,並且最後需要統一彙總的時候,我們可以使用CyclicBarrier;CyclicBarrier可以構造一個集結點,當某一個線程執行完畢,它就會到集結點等待,直到所有線程都到達集結點,那麼該柵欄就被撤銷,所有的線程再統一出發執行剩餘的任務。
示例
public class CyclicbarrierTest {
private static Vector<String> vector = new Vector<String>();
public static void main(String[] args) {
//創建容量爲5的CyclicBarrier
CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new Runnable() {
@Override
public void run() {
System.out.println("所有線程處理完畢");
System.out.println(vector.toString());
}
});
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
executorService.submit(new Task(vector,cyclicBarrier));
}
//while (executorService.isTerminated()){}
executorService.shutdown();
}
static class Task implements Runnable{
private Vector<String> vector = new Vector<String>();
private CyclicBarrier cyclicBarrier;
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"開始處理數據");
try {
Thread.sleep(new Random().nextInt(10)*1000);
vector.add(Thread.currentThread().getName());
System.out.println(Thread.currentThread().getName()+"數據處理完畢");
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
public Vector<String> getVector() {
return vector;
}
public void setVector(Vector<String> vector) {
this.vector = vector;
}
public CyclicBarrier getCyclicBarrier() {
return cyclicBarrier;
}
public void setCyclicBarrier(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier;
}
public Task(Vector<String> vector, CyclicBarrier cyclicBarrier) {
this.vector = vector;
this.cyclicBarrier = cyclicBarrier;
}
}
}
【如果有5個以上的線程,也會進行5個一處理】
CyclicBarrier與CountDownLatch的區別
作用不同:CyclicBarrier要等固定數量的線程都到達了柵欄位置才能繼續執行,而CountDownLatch只需要等到數字爲0,也就是CyclicBarrier作用於線程CountDownLatch作用於方法。
可重用性:CountDownLatch不可重複使用,而CyclicBarrier可以重複使用。