JUC只線程之間協作

這個是在學習工作中的一些總結,若有不對之處歡迎大家指出。侵刪!
需要源碼聯繫QQ:1352057131
得之在俄頃,積之在平日。

 

目錄

控制併發流程

控制併發流程工具類概覽

CountDownLatch

作用

流程

主要方法

示例1

示例2

示例3

Semaphore

作用

使用流程

常用方法

示例

Condition接口(條件對象)

作用

常用方法

示例

示例2:消費者生產者模式

注意點

CyclicBarrier循環柵欄

作用

示例

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可以重複使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章