JUC三大輔助類(CountDownLatch、CyclicBarrier和Semaphore)、ReadWriteLock(讀寫鎖)和BlockingQueue(阻塞隊列)

JUC三大輔助類

一、CountDownLatch:減少計數方法

作用:讓一些線程阻塞直到另一些線程完成一系列操作後才被喚醒。

提供的三個方法:

  1. new CountDownLatch(6);設置計數器爲6個線程
  2. countDown();將計數器減1(調用countDown方法的線程不會阻塞)
  3. await();計數器的值>0,線程會阻塞,當計數器的值=0時,因await()方法阻塞的線程會被喚醒,繼續執行
import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo
{
   public static void main(String[] args) throws InterruptedException
   {
         CountDownLatch countDownLatch = new CountDownLatch(6);
       
       for (int i = 1; i <=6; i++) //6個上自習的同學,各自離開教室的時間不一致
       {
          new Thread(() -> {
              System.out.println(Thread.currentThread().getName()+"\t 號同學離開教室");
			//線程調用countDown()方法會將計數器減1(調用countDown方法的線程不會阻塞)
              countDownLatch.countDown();
          }, String.valueOf(i)).start();
       }
       //線程調用await()方法時,這些線程會阻塞
       countDownLatch.await();
       //當計數器的值變爲0時,因await()方法阻塞的線程會被喚醒,繼續執行
       System.out.println(Thread.currentThread().getName()+"\t****** 班長關門走人,main線程是班長");          
   }
}

二、CyclicBarrier:循環柵欄方法

作用:讓一組線程到達一個屏障(也可以叫同步點)時被阻塞,直到最後一個線程到達屏障時,屏障纔會開門,所有被屏障攔截的線程纔會繼續執行。

提供的方法:

  1. new CyclicBarrier(int parties, Runnable barrierAction);構造方法中:需要類實現Runnable接口
  2. cyclicBarrier.await();同步點阻塞
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo
{
  private static final int NUMBER = 7;
  public static void main(String[] args)
  {
     //構造器:CyclicBarrier(int parties, Runnable barrierAction) 
     CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER, ()->{System.out.println("*****集齊7顆龍珠就可以召喚神龍");}) ;

     for (int i = 1; i <= 7; i++) {
       new Thread(() -> {
          try {
            System.out.println(Thread.currentThread().getName()+"\t 星龍珠被收集 ");
            //阻塞點 awaitCurrent >= 7 ? openAwait() : await();
            cyclicBarrier.await();
          } catch (InterruptedException | BrokenBarrierException e) {
            e.printStackTrace();
          }
       }, String.valueOf(i)).start();
     }
  }
}

三、Semaphore:信號燈

作用:

  1. 用於多個共享資源的互斥使用
  2. 用於併發線程數的控制。

提供的方法:

  1. new Semaphore(3);//模擬3個資源,可申請使用可釋放
  2. acquire();申請,當一個線程調用acquire()操作時,它要麼通過,成功獲取信號量(信號量減1),要麼一直等下去,直到有線程釋放信號量,或超時
  3. release();釋放,使用在finally{}中,會將信號量的值加1,然後喚醒等待的線程
import java.util.Random;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SemaphoreDemo
{
  public static void main(String[] args)
  {
     Semaphore semaphore = new Semaphore(3);//模擬3個停車位

     for (int i = 1; i <=6; i++) //模擬6部汽車
     {
       new Thread(() -> {
          try 
          {
            semaphore.acquire();//申請獲取資源
            System.out.println(Thread.currentThread().getName()+"\t 搶到了車位");
            TimeUnit.SECONDS.sleep(new Random().nextInt(5));
            System.out.println(Thread.currentThread().getName()+"\t------- 離開");
          } catch (InterruptedException e) {
            e.printStackTrace();
          }finally {
            //釋放資源信號量,喚醒等待線程
            semaphore.release();
          }
       }, String.valueOf(i)).start();
     }
  }
}

ReadWriteLock:讀寫鎖

  • import java.util.concurrent.locks.*;三種鎖:
    • Condition
    • Lock
    • ReadWriteLock

作用:多線程同時讀寫數據時會出現混亂,用讀寫鎖將讀寫分開,讓寫分離,讀讀共享

提供方法:

  1. ReadWriteLock rwLock = new ReentrantReadWriteLock();實現類構造器
  2. 寫鎖writeLockrwLock.writeLock().lock();,在finally{}處解鎖:rwLock.writeLock().unlock();
  3. 讀鎖readLockrwLock.readLock().lock();,在finally{}處解鎖:rwLock.readLock().unlock();
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

class MyCache {
    private volatile Map<String, Object> map = new HashMap<>();
    private ReadWriteLock rwLock = new ReentrantReadWriteLock();

    public void put(String key, Object value) {
        rwLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "\t 正在寫" + key);
            //暫停一會兒線程
            try {
                TimeUnit.MILLISECONDS.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + "\t 寫完了" + key);
            System.out.println();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rwLock.writeLock().unlock();
        }
    }

    public Object get(String key) {
        rwLock.readLock().lock();
        Object result = null;
        try {
            System.out.println(Thread.currentThread().getName() + "\t 正在讀" + key);
            try {
                TimeUnit.MILLISECONDS.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            result = map.get(key);
            System.out.println(Thread.currentThread().getName() + "\t 讀完了" + result);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rwLock.readLock().unlock();
        }
        return result;
    }
}

public class ReadWriteLockDemo {
    public static void main(String[] args) {
        MyCache myCache = new MyCache();

        for (int i = 1; i <= 5; i++) {
            final int num = i;
            new Thread(() -> {
                myCache.put(num + "", num + "");
            }, String.valueOf(i)).start();
        }
        for (int i = 1; i <= 5; i++) {
            final int num = i;
            new Thread(() -> {
                myCache.get(num + "");
            }, String.valueOf(i)).start();
        }
    }
}

BlockingQueue:阻塞隊列

當隊列是空的,從隊列中獲取元素的操作將會被阻塞

當隊列是滿的,從隊列中添加元素的操作將會被阻塞

  • Collection

    • Queue
      • BlockingQueue:阻塞隊列
        • ArrayBlockingQueue:數組結構阻塞隊列
        • LinkedBlockingQueue:鏈表結構阻塞隊列(大小默認值爲Integer.MAX_VALUE
        • SynchronousQueue:單元素阻塞隊列
        • PriorityBlockingQueue:優先級排序無界阻塞隊列
        • DelayQueue:優先級延遲無界阻塞隊列
        • LinkedTransferQueue:鏈表無界阻塞隊列
        • LinkedBlockingDeque:鏈表雙向阻塞隊列
  • BlockingQueue的方法:

    • 創建方法:BlockingQueue<String> Queue = new ArrayBlockingQueue<>(int initSize);
方法類型 拋出異常 返回特殊值 阻塞 超時
插入 boolean add(e) boolean offer(e) void put(e) boolean offer(e,timeout,timeunit)
刪除 E remove() E poll() E take() boolean poll(e,timeout,timeunit)
檢查 E element() E peek() / /
類型 說明
拋出異常 1. 當阻塞隊列滿時,再往隊列裏add插入元素
會拋IllegalStateException:Queue full
2. 當阻塞隊列空時,再往隊列裏remove移除元素
會拋NoSuchElementException
特殊值 1. 插入方法,成功ture失敗false
2. 刪除方法,成功返回出隊列的元素,隊列裏沒有就返回null
一直阻塞 1. 當阻塞隊列滿時,生產者線程繼續往隊列裏put元素,
隊列會一直阻塞生產者線程直到put數據or響應中斷退出
2. 當阻塞隊列空時,消費者線程試圖從隊列裏take元素,
隊列會一直阻塞消費者線程直到隊列可用
超時退出 當阻塞隊列滿時,隊列會阻塞生產者線程一定時間,超過限時後生產者線程會退出
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章