Conditon用法整理

       關鍵字syncronized與wait()和notify()和notifyAll()可以實現等待通知,類ReentrantLock也可以實現同樣的功能,但需要藉助於Condition,這個類可以實現多路通知,也就是說在一個Lock對象裏可以創建多個Condition實例(對象監視器),線程對象可以註冊在指定的Condition中,從而可以選擇性的通知線程在線程調度上更加靈活。

       而syncronized就相等於整個Lock對象中只有單一的一個Condition對象,所有的線程都註冊在它一個上面,線程開始notifyAll()時,需要通知所有的WAITING線程,沒有選擇權,就會出現資源浪費。下面一個Demo演示的是Condition的錯誤用法。

public class Demo1 {

    private Lock lock = new ReentrantLock ();
    private Condition condition = lock.newCondition ();

    public void await(){
        try {
            condition.await ();
        } catch (InterruptedException e) {
            e.printStackTrace ();
        }
    }

    public static void main(String[] args) {
        Demo1 demo1 = new Demo1 ();
        new Thread (() -> {
            demo1.await ();
        }).start ();

    }

}

運行後報錯:因爲在調用Condition.awiat()或single()方法之前需要調用lock.lock()獲得同步監視器,切記,跟我們在使用wait方法是同時需要使用synctonized關鍵字一樣一個道理

Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
	at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:151)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.fullyRelease(AbstractQueuedSynchronizer.java:1723)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2036)
	at Lock.KodyLockTest.ConditionStudy.Demo1.await(Demo1.java:19)
	at Lock.KodyLockTest.ConditionStudy.Demo1.lambda$main$0(Demo1.java:28)
	at java.lang.Thread.run(Thread.java:748)

雙線程通信案例

public class Demo1 {

    private Lock lock = new ReentrantLock ();
    private Condition condition = lock.newCondition ();

    public void await(){
        try {
            lock.lock ();
            System.out.println (Thread.currentThread ().getName () + "線程獲得鎖");
            condition.await ();
        } catch (InterruptedException e) {
            e.printStackTrace ();
        }finally {
            System.out.println (Thread.currentThread ().getName () + "線程釋放鎖");
            lock.unlock ();
        }
    }

    public void single(){
        lock.lock ();
        System.out.println (Thread.currentThread ().getName () + "線程獲得鎖");
        condition.signal ();
        System.out.println (Thread.currentThread ().getName () + "線程喚醒睡眠線程");
        lock.unlock ();
        System.out.println (Thread.currentThread ().getName () + "線程釋放鎖");
    }

    public static void main(String[] args) throws InterruptedException{
        Demo1 demo1 = new Demo1 ();
        new Thread (() -> {
            demo1.await ();
        }).start ();
        TimeUnit.SECONDS.sleep (3);

        new Thread (() -> {
            demo1.single ();
        }).start ();

    }

}

控制檯信息:首先線程0獲得鎖後進入等待(意味着放棄了lock對象鎖),而後被線程1搶得,線程1接下來喚醒了等待池中等待的線程,此處值得是註冊在condition上的線程,目前就只有一個線程0,所以會被喚醒,由於喚醒了線程0但是喚醒了你要繼續執行線程0的run代碼需要再次獲得lock對象鎖,這就得看線程1釋放沒釋放了,釋放了的話線程0繼續搶鎖,搶到的話執行完run代碼最後也釋放。這裏需要記住的一點就是線程1喚醒線程0並不能立即執行run代碼,因爲你需要再次搶鎖切記,需要拿到鎖才能被喚醒。

Thread-0線程獲得鎖
Thread-1線程獲得鎖
Thread-1線程喚醒睡眠線程
Thread-1線程釋放鎖
Thread-0線程釋放鎖

雙線程交替執行

   使用一個Condition對象加一個標識符來實現交替運行

package Lock.KodyLockTest.ConditionStudy;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author Heian
 * @time 19/10/13 16:13
 * @description:線程一打印循環打印 我愛你  線程2打印 肖體秀
 */
public class Demo2 {

    private static Lock lock = new ReentrantLock ();
    private static Condition condition = lock.newCondition ();
    //設置一個標識符,如果某個線程打印了則修改標識符位,讓自身處於等待,同理另一個線程可以打印
    private static Boolean flag = false;//因爲用了線程同步,所以該字面修改是線程可見的

    public static void main(String[] args) {

        new Thread (() -> {
            lock.lock ();
            for (int i=0;i<100;i++){
                try {
                    while (flag == true){
                        condition.await ();
                    }
                    System.out.println ("我愛你");
                    TimeUnit.SECONDS.sleep (1);
                    flag = true;
                    condition.signal ();
                }catch (InterruptedException e){
                    e.printStackTrace ();
                }
            }
            lock.unlock ();
        }).start ();


        new Thread (() -> {
            lock.lock ();
            for (int i=0;i<100;i++){
                try {
                    while (flag == false){
                        condition.await ();
                    }
                    System.out.println ("肖體秀");
                    TimeUnit.SECONDS.sleep (1);
                    flag = false;
                    condition.signal ();
                }catch (InterruptedException e){
                    e.printStackTrace ();
                }
            }
            lock.unlock ();
        }).start ();
    }

}

    使用兩個Condition對象實現,因爲要結束線程聲明週期,所以加了個!=10的判斷,直接釋放鎖,而不是還讓線程處於等待

package Lock.KodyLockTest.ConditionStudy;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author Heian
 * @time 19/10/13 16:13
 * @description:線程一打印循環打印 我愛你  線程2打印 肖體秀
 */
public class Demo3 {

    private static Lock lock = new ReentrantLock ();
    private static Condition conditionA = lock.newCondition ();
    private static Condition conditionB = lock.newCondition ();
    private static AtomicInteger atomicIntegerA = new AtomicInteger (0);
    private static AtomicInteger atomicIntegerB = new AtomicInteger (0);

    public static void main(String[] args) {

        new Thread (() -> {
            lock.lock ();
            for (int i=0;i<10;i++){
                try {
                    TimeUnit.SECONDS.sleep (1);
                    System.out.println ("我愛你");
                    conditionB.signal ();//在進入等待池之前先喚醒B,此時A的鎖並沒有釋放
                    if ( atomicIntegerA.incrementAndGet () != 10){
                        conditionA.await ();//如果不是最後一個需要你睡眠,是最後一次的話就直接unlock
                    }
                }catch (InterruptedException e){
                    e.printStackTrace ();
                }
            }
            lock.unlock ();
        }).start ();


        new Thread (() -> {
            lock.lock ();
            for (int i=0;i<10;i++){
                try {
                    TimeUnit.SECONDS.sleep (1);
                    System.out.println ("肖體秀");
                    conditionA.signal ();//在進入等待池之前先喚醒A,此時B的鎖並沒有釋放
                    if ( atomicIntegerB.incrementAndGet () != 10){
                        conditionB.await ();
                    }
                }catch (InterruptedException e){
                    e.printStackTrace ();
                }
            }
            lock.unlock ();
        }).start ();

    }

}

三個及以上線程交替執行與順序執行

       交替執行案例可以參考我的:https://blog.csdn.net/qq_40826106/article/details/86607122 案例7  這裏我沒做線程結束判斷,自己手動加下就行,那順序執行也是一樣的。

   這裏隨機運行三個線程,通過變量num去指定那個線程順序執行:三個線程隨機啓動,假設B先啓動,B會處於阻塞,假設然後C啓動,C也會處於阻塞,當運行到A啓動時,會喚醒B,B執行完喚醒C,以此達到ABC順序運行。

package Lock.KodyLockTest.ConditionStudy;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author Heian
 * @time 19/10/13 16:13
 * @description: 線程順序執行
 */
public class Demo4 {

    private static Lock lock = new ReentrantLock ();
    private static Condition conditionA = lock.newCondition ();
    private static Condition conditionB = lock.newCondition ();
    private static Condition conditionC = lock.newCondition ();
    private static int num = 1;

    public static void main(String[] args) {

        Thread threadA = new Thread (() -> {
            try {
                lock.lock ();
                while (num != 1) {
                    conditionA.await ();
                }
                System.out.println ("我愛你肖體秀1");
                num = 2;//事情做完了,就賦值爲2
                conditionB.signal ();
            } catch (InterruptedException e) {
                e.printStackTrace ();
            } finally {
                lock.unlock ();
            }
        });

        Thread threadB = new Thread (() -> {
            try {
                lock.lock ();
                while (num != 2) {
                    conditionB.await ();
                }
                System.out.println ("我愛你肖體秀2");
                num = 3;
                conditionC.signal ();
            } catch (InterruptedException e) {
                e.printStackTrace ();
            } finally {
                lock.unlock ();
            }
        });

        Thread threadC = new Thread (() -> {
            try {
                lock.lock ();
                while (num != 3) {
                    conditionC.await ();
                }
                System.out.println ("我愛你肖體秀3");
                num = 3;
                conditionA.signal ();
            } catch (InterruptedException e) {
                e.printStackTrace ();
            } finally {
                lock.unlock ();
            }
        });
        threadB.start ();
        threadA.start ();
        threadC.start ();

    }

}

 

 

 

 

 

 

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