使用ReentrantLock實現阻塞隊列與交替打印


一、前言

  1.ReentrantLock是可重入鎖,意味着一個線程可以進入任何一個該線程已擁有的鎖同步着的代碼塊,實現了Lock接口,通過Condition精細控制多線程休眠喚醒。

  2.Lock接口

    //獲取鎖,獲取不到lock就不罷休,不可被打斷,即使當前線程被中斷,線程也一直阻塞,直到拿到鎖, 比較無賴的做法。
    void lock();
    
   /**
   *獲取鎖,可中斷,如果獲取鎖之前當前線程被interrupt了,
   *獲取鎖之后會拋出InterruptedException,並且停止當前線程;
   *優先響應中斷
   */
    void lockInterruptibly() throws InterruptedException;

    //立即返回結果;嘗試獲得鎖,如果獲得鎖立即返回ture,失敗立即返回false
    boolean tryLock();

    //嘗試拿鎖,可設置超時時間,超時返回false,即過時不候
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

    //釋放鎖
    void unlock();
    
    //返回當前線程的Condition ,可多次調用
    Condition newCondition();

  3.Condition接口

public interface Condition {
    /**
    *Condition線程進入阻塞狀態,調用signal()或者signalAll()再次喚醒,
    *允許中斷如果在阻塞時鎖持有線程中斷,會拋出異常;
    *重要一點是:在當前持有Lock的線程中,當外部調用會await()后,ReentrantLock就允許其他線程來搶奪鎖當前鎖,
    *注意:通過創建Condition對象來使線程wait,必須先執行lock.lock方法獲得鎖
    */
    void await() throws InterruptedException;

    //Condition線程進入阻塞狀態,調用signal()或者signalAll()再次喚醒,不允許中斷,如果在阻塞時鎖持有線程中斷,繼續等待喚醒
    void awaitUninterruptibly();

    //設置阻塞時間,超時繼續,超時時間單位為納秒,其他同await();返回時間大於零,表示是被喚醒,等待時間並且可以作為等待時間期望值,小於零表示超時
    long awaitNanos(long nanosTimeout) throws InterruptedException;

    //類似awaitNanos(long nanosTimeout);返回值:被喚醒true,超時false
    boolean await(long time, TimeUnit unit) throws InterruptedException;

   //類似await(long time, TimeUnit unit) 
    boolean awaitUntil(Date deadline) throws InterruptedException;

   //喚醒指定線程
    void signal();
    
    //喚醒全部線程
    void signalAll();
}

二、實現阻塞隊列

  1.實現

public class BlockQueue {
    //隊列容器
    private List<Integer> container = new ArrayList<>();
    private Lock lock = new ReentrantLock();
    //隊列為空
    private Condition isNull = lock.newCondition();
    //隊列已滿
    private Condition isFull = lock.newCondition();
    private volatile int size;
    private volatile int capacity;

    BlockQueue(int cap){
        this.capacity = cap;
    }

    public void add(int data){
        try {
            lock.lock();
            while(size>=capacity){
                System.out.println("隊列已滿,釋放鎖,等待消費者消費");
                try {
                    isFull.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            ++size;
            container.add(data);
            isNull.signal();
        } finally {
            lock.unlock();
        }
    }

    public int take(){
        try {
            lock.lock();
            while(0==size){
                System.out.println("阻塞隊列空了,釋放鎖,等待生產者生產數據");
                try {
                    isNull.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            --size;
            int res = container.get(0);
            container.remove(0);
            isFull.signal();
            return res;
        }finally {
            lock.unlock();
        }
    }
}

  2.測試與運行結果

public class BlockQueueTest {
    public static void main(String[] args) {
        BlockQueue queue = new BlockQueue(5);
        Thread thread1 = new Thread(()->{
           for(int i=0;i<100;i++){
               queue.add(i);
               System.out.println("生產:" + i);
               try {
                   Thread.sleep(300);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
        });
        Thread thread2 = new Thread(()->{
            for(;;){
                System.out.println("消費者開始工作,消費:" + queue.take());
                try {
                    Thread.sleep(600);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        thread1.start();
        thread2.start();
    }
}

  測試結果:

 

 

 

 三、交替打印

  1.實現

public class AlternateTask {
    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        Condition condition1 = lock.newCondition();
        Condition condition2 = lock.newCondition();
        Thread thread1 = new Thread(()->{
            try {
                lock.lock();
                for (int i=1;i<100;i+=2){
                   System.out.println("-----thread1-----" + i );
                   condition1.await();
                   condition2.signal();
                   Thread.sleep(500);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        });
        Thread thread2 = new Thread(()->{
           try {
               lock.lock();
               for(int i=2;i<101;i+=2){
                   System.out.println("-----thread2-----" + i);
                   condition1.signal();
                   condition2.await();
                   Thread.sleep(500);
               }
           } catch (InterruptedException e){
               e.getStackTrace();
           } finally {
               lock.unlock();
           }

        });
        thread1.start();
        thread2.start();
    }
}

  2測試與運行結果

 

 

 

 四、結語

  本文是對ReentrantLock的兩個經典demo做簡單實現,如有紕漏請及時指正,一起學習共同進步。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM