亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

java高并發系列 - 第13天:JUC中的Condition對象

發布時間:2020-07-22 18:34:37 來源:網絡 閱讀:273 作者:路人甲Java 欄目:編程語言

本文目標:

  1. synchronized中實現線程等待和喚醒
  2. Condition簡介及常用方法介紹及相關示例
  3. 使用Condition實現生產者消費者
  4. 使用Condition實現同步阻塞隊列

Object對象中的wait(),notify()方法,用于線程等待和喚醒等待中的線程,大家應該比較熟悉,想再次了解的朋友可以移步到線程的基本操作

synchronized中等待和喚醒線程示例

package com.itsoku.chat09;

import java.util.concurrent.TimeUnit;

/**
 * 微信公眾號:javacode2018,獲取年薪50萬課程
 */
public class Demo1 {
    static Object lock = new Object();

    public static class T1 extends Thread {
        @Override
        public void run() {
            System.out.println(System.currentTimeMillis() + "," + this.getName() + "準備獲取鎖!");
            synchronized (lock) {
                System.out.println(System.currentTimeMillis() + "," + this.getName() + "獲取鎖成功!");
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(System.currentTimeMillis() + "," + this.getName() + "釋放鎖成功!");
        }
    }

    public static class T2 extends Thread {
        @Override
        public void run() {
            System.out.println(System.currentTimeMillis() + "," + this.getName() + "準備獲取鎖!");
            synchronized (lock) {
                System.out.println(System.currentTimeMillis() + "," + this.getName() + "獲取鎖成功!");
                lock.notify();
                System.out.println(System.currentTimeMillis() + "," + this.getName() + " notify!");
                try {
                    TimeUnit.SECONDS.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(System.currentTimeMillis() + "," + this.getName() + "準備釋放鎖!");
            }
            System.out.println(System.currentTimeMillis() + "," + this.getName() + "釋放鎖成功!");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        T1 t1 = new T1();
        t1.setName("t1");
        t1.start();
        TimeUnit.SECONDS.sleep(5);
        T2 t2 = new T2();
        t2.setName("t2");
        t2.start();
    }
}

輸出:

1:1563530109234,t1準備獲取鎖!
2:1563530109234,t1獲取鎖成功!
3:1563530114236,t2準備獲取鎖!
4:1563530114236,t2獲取鎖成功!
5:1563530114236,t2 notify!
6:1563530119237,t2準備釋放鎖!
7:1563530119237,t2釋放鎖成功!
8:1563530119237,t1釋放鎖成功!

代碼結合輸出的結果我們分析一下:

  1. 線程t1先獲取鎖,然后調用了wait()方法將線程置為等待狀態,然后會釋放lock的鎖
  2. 主線程等待5秒之后,啟動線程t2,t2獲取到了鎖,結果中1、3行時間相差5秒左右
  3. t2調用lock.notify()方法,準備將等待在lock上的線程t1喚醒,notify()方法之后又休眠了5秒,看一下輸出的5、8可知,notify()方法之后,t1并不能立即被喚醒,需要等到t2將synchronized塊執行完畢,釋放鎖之后,t1才被喚醒
  4. wait()方法和notify()方法必須放在同步塊內調用(synchronized塊內),否則會報錯

Condition使用簡介

在了解Condition之前,需要先了解一下重入鎖ReentrantLock,可以移步到:JUC中的ReentranLock。

任何一個java對象都天然繼承于Object類,在線程間實現通信的往往會應用到Object的幾個方法,比如wait()、wait(long timeout)、wait(long timeout, int nanos)與notify()、notifyAll()幾個方法實現等待/通知機制,同樣的, 在java Lock體系下依然會有同樣的方法實現等待/通知機制。

從整體上來看Object的wait和notify/notify是與對象監視器配合完成線程間的等待/通知機制,而Condition與Lock配合完成等待通知機制,前者是java底層級別的,后者是語言級別的,具有更高的可控制性和擴展性。兩者除了在使用方式上不同外,在功能特性上還是有很多的不同:

  1. Condition能夠支持不響應中斷,而通過使用Object方式不支持
  2. Condition能夠支持多個等待隊列(new 多個Condition對象),而Object方式只能支持一個
  3. Condition能夠支持超時時間的設置,而Object不支持

Condition由ReentrantLock對象創建,并且可以同時創建多個,Condition接口在使用前必須先調用ReentrantLock的lock()方法獲得鎖,之后調用Condition接口的await()將釋放鎖,并且在該Condition上等待,直到有其他線程調用Condition的signal()方法喚醒線程,使用方式和wait()、notify()類似。

示例代碼:

package com.itsoku.chat09;

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

/**
 * 微信公眾號:javacode2018,獲取年薪50萬課程
 */
public class Demo2 {
    static ReentrantLock lock = new ReentrantLock();
    static Condition condition = lock.newCondition();

    public static class T1 extends Thread {
        @Override
        public void run() {
            System.out.println(System.currentTimeMillis() + "," + this.getName() + "準備獲取鎖!");
            lock.lock();
            try {
                System.out.println(System.currentTimeMillis() + "," + this.getName() + "獲取鎖成功!");
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
            System.out.println(System.currentTimeMillis() + "," + this.getName() + "釋放鎖成功!");
        }
    }

    public static class T2 extends Thread {
        @Override
        public void run() {
            System.out.println(System.currentTimeMillis() + "," + this.getName() + "準備獲取鎖!");
            lock.lock();
            try {
                System.out.println(System.currentTimeMillis() + "," + this.getName() + "獲取鎖成功!");
                condition.signal();
                System.out.println(System.currentTimeMillis() + "," + this.getName() + " signal!");
                try {
                    TimeUnit.SECONDS.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(System.currentTimeMillis() + "," + this.getName() + "準備釋放鎖!");
            } finally {
                lock.unlock();
            }
            System.out.println(System.currentTimeMillis() + "," + this.getName() + "釋放鎖成功!");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        T1 t1 = new T1();
        t1.setName("t1");
        t1.start();
        TimeUnit.SECONDS.sleep(5);
        T2 t2 = new T2();
        t2.setName("t2");
        t2.start();
    }
}

輸出:

1563532185827,t1準備獲取鎖!
1563532185827,t1獲取鎖成功!
1563532190829,t2準備獲取鎖!
1563532190829,t2獲取鎖成功!
1563532190829,t2 signal!
1563532195829,t2準備釋放鎖!
1563532195829,t2釋放鎖成功!
1563532195829,t1釋放鎖成功!

輸出的結果和使用synchronized關鍵字的實例類似。

Condition.await()方法和Object.wait()方法類似,當使用Condition.await()方法時,需要先獲取Condition對象關聯的ReentrantLock的鎖,在Condition.await()方法被調用時,當前線程會釋放這個鎖,并且當前線程會進行等待(處于阻塞狀態)。在signal()方法被調用后,系統會從Condition對象的等待隊列中喚醒一個線程,一旦線程被喚醒,被喚醒的線程會嘗試重新獲取鎖,一旦獲取成功,就可以繼續執行了。因此,在signal被調用后,一般需要釋放相關的鎖,讓給其他被喚醒的線程,讓他可以繼續執行。

Condition常用方法

Condition接口提供的常用方法有:

和Object中wait類似的方法

  1. void await() throws InterruptedException:當前線程進入等待狀態,如果其他線程調用condition的signal或者signalAll方法并且當前線程獲取Lock從await方法返回,如果在等待狀態中被中斷會拋出被中斷異常;
  2. long awaitNanos(long nanosTimeout):當前線程進入等待狀態直到被通知,中斷或者超時
  3. boolean await(long time, TimeUnit unit) throws InterruptedException:同第二種,支持自定義時間單位,false:表示方法超時之后自動返回的,true:表示等待還未超時時,await方法就返回了(超時之前,被其他線程喚醒了)
  4. boolean awaitUntil(Date deadline) throws InterruptedException:當前線程進入等待狀態直到被通知,中斷或者到了某個時間
  5. void awaitUninterruptibly();:當前線程進入等待狀態,不會響應線程中斷操作,只能通過喚醒的方式讓線程繼續

和Object的notify/notifyAll類似的方法

  1. void signal():喚醒一個等待在condition上的線程,將該線程從等待隊列中轉移到同步隊列中,如果在同步隊列中能夠競爭到Lock則可以從等待方法中返回。
  2. void signalAll():與1的區別在于能夠喚醒所有等待在condition上的線程

Condition.await()過程中被打斷

package com.itsoku.chat09;

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

/**
 * 微信公眾號:javacode2018,獲取年薪50萬課程
 */
public class Demo4 {
    static ReentrantLock lock = new ReentrantLock();
    static Condition condition = lock.newCondition();

    public static class T1 extends Thread {
        @Override
        public void run() {
            lock.lock();
            try {
                condition.await();
            } catch (InterruptedException e) {
                System.out.println("中斷標志:" + this.isInterrupted());
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        T1 t1 = new T1();
        t1.setName("t1");
        t1.start();
        TimeUnit.SECONDS.sleep(2);
        //給t1線程發送中斷信號
        System.out.println("1、t1中斷標志:" + t1.isInterrupted());
        t1.interrupt();
        System.out.println("2、t1中斷標志:" + t1.isInterrupted());
    }
}

輸出:

1、t1中斷標志:false
2、t1中斷標志:true
中斷標志:false
java.lang.InterruptedException
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.reportInterruptAfterWait(AbstractQueuedSynchronizer.java:2014)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2048)
    at com.itsoku.chat09.Demo4$T1.run(Demo4.java:19)

調用condition.await()之后,線程進入阻塞中,調用t1.interrupt(),給t1線程發送中斷信號,await()方法內部會檢測到線程中斷信號,然后觸發InterruptedException異常,線程中斷標志被清除。從輸出結果中可以看出,線程t1中斷標志的變換過程:false->true->false

await(long time, TimeUnit unit)超時之后自動返回

package com.itsoku.chat09;

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

/**
 * 微信公眾號:javacode2018,獲取年薪50萬課程
 */
public class Demo5 {
    static ReentrantLock lock = new ReentrantLock();
    static Condition condition = lock.newCondition();

    public static class T1 extends Thread {
        @Override
        public void run() {
            lock.lock();
            try {
                System.out.println(System.currentTimeMillis() + "," + this.getName() + ",start");
                boolean r = condition.await(2, TimeUnit.SECONDS);
                System.out.println(r);
                System.out.println(System.currentTimeMillis() + "," + this.getName() + ",end");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        T1 t1 = new T1();
        t1.setName("t1");
        t1.start();
    }
}

輸出:

1563541624082,t1,start
false
1563541626085,t1,end

t1線程等待2秒之后,自動返回繼續執行,最后await方法返回false,await返回false表示超時之后自動返回

await(long time, TimeUnit unit)超時之前被喚醒

package com.itsoku.chat09;

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

/**
 * 微信公眾號:javacode2018,獲取年薪50萬課程
 */
public class Demo6 {
    static ReentrantLock lock = new ReentrantLock();
    static Condition condition = lock.newCondition();

    public static class T1 extends Thread {
        @Override
        public void run() {
            lock.lock();
            try {
                System.out.println(System.currentTimeMillis() + "," + this.getName() + ",start");
                boolean r = condition.await(5, TimeUnit.SECONDS);
                System.out.println(r);
                System.out.println(System.currentTimeMillis() + "," + this.getName() + ",end");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        T1 t1 = new T1();
        t1.setName("t1");
        t1.start();
        //休眠1秒之后,喚醒t1線程
        TimeUnit.SECONDS.sleep(1);
        lock.lock();
        try {
            condition.signal();
        } finally {
            lock.unlock();
        }
    }
}

輸出:

1563542046046,t1,start
true
1563542047048,t1,end

t1線程中調用condition.await(5, TimeUnit.SECONDS);方法會釋放鎖,等待5秒,主線程休眠1秒,然后獲取鎖,之后調用signal()方法喚醒t1,輸出結果中發現await后過了1秒(1、3行輸出結果的時間差),await方法就返回了,并且返回值是true。true表示await方法超時之前被其他線程喚醒了。

long awaitNanos(long nanosTimeout)超時返回

package com.itsoku.chat09;

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

/**
 * 微信公眾號:javacode2018,獲取年薪50萬課程
 */
public class Demo7 {
    static ReentrantLock lock = new ReentrantLock();
    static Condition condition = lock.newCondition();

    public static class T1 extends Thread {
        @Override
        public void run() {
            lock.lock();
            try {
                System.out.println(System.currentTimeMillis() + "," + this.getName() + ",start");
                long r = condition.awaitNanos(TimeUnit.SECONDS.toNanos(5));
                System.out.println(r);
                System.out.println(System.currentTimeMillis() + "," + this.getName() + ",end");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        T1 t1 = new T1();
        t1.setName("t1");
        t1.start();
    }
}

輸出:

1563542547302,t1,start
-258200
1563542552304,t1,end

awaitNanos參數為納秒,可以調用TimeUnit中的一些方法將時間轉換為納秒。

t1調用await方法等待5秒超時返回,返回結果為負數,表示超時之后返回的。

waitNanos(long nanosTimeout)超時之前被喚醒

package com.itsoku.chat09;

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

/**
 * 微信公眾號:javacode2018,獲取年薪50萬課程
 */
public class Demo8 {
    static ReentrantLock lock = new ReentrantLock();
    static Condition condition = lock.newCondition();

    public static class T1 extends Thread {
        @Override
        public void run() {
            lock.lock();
            try {
                System.out.println(System.currentTimeMillis() + "," + this.getName() + ",start");
                long r = condition.awaitNanos(TimeUnit.SECONDS.toNanos(5));
                System.out.println(r);
                System.out.println(System.currentTimeMillis() + "," + this.getName() + ",end");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        T1 t1 = new T1();
        t1.setName("t1");
        t1.start();
        //休眠1秒之后,喚醒t1線程
        TimeUnit.SECONDS.sleep(1);
        lock.lock();
        try {
            condition.signal();
        } finally {
            lock.unlock();
        }
    }
}

輸出:

1563542915991,t1,start
3999988500
1563542916992,t1,end

t1中調用await休眠5秒,主線程休眠1秒之后,調用signal()喚醒線程t1,await方法返回正數,表示返回時距離超時時間還有多久,將近4秒,返回正數表示,線程在超時之前被喚醒了。

其他幾個有參的await方法和無參的await方法一樣,線程調用interrupt()方法時,這些方法都會觸發InterruptedException異常,并且線程的中斷標志會被清除。

同一個鎖支持創建多個Condition

使用兩個Condition來實現一個阻塞隊列的例子:

package com.itsoku.chat09;

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

/**
 * 微信公眾號:javacode2018,獲取年薪50萬課程
 */
public class BlockingQueueDemo<E> {
    int size;//阻塞隊列最大容量

    ReentrantLock lock = new ReentrantLock();

    LinkedList<E> list = new LinkedList<>();//隊列底層實現

    Condition notFull = lock.newCondition();//隊列滿時的等待條件
    Condition notEmpty = lock.newCondition();//隊列空時的等待條件

    public BlockingQueueDemo(int size) {
        this.size = size;
    }

    public void enqueue(E e) throws InterruptedException {
        lock.lock();
        try {
            while (list.size() == size)//隊列已滿,在notFull條件上等待
                notFull.await();
            list.add(e);//入隊:加入鏈表末尾
            System.out.println("入隊:" + e);
            notEmpty.signal(); //通知在notEmpty條件上等待的線程
        } finally {
            lock.unlock();
        }
    }

    public E dequeue() throws InterruptedException {
        E e;
        lock.lock();
        try {
            while (list.size() == 0)//隊列為空,在notEmpty條件上等待
                notEmpty.await();
            e = list.removeFirst();//出隊:移除鏈表首元素
            System.out.println("出隊:" + e);
            notFull.signal();//通知在notFull條件上等待的線程
            return e;
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        BlockingQueueDemo<Integer> queue = new BlockingQueueDemo<>(2);
        for (int i = 0; i < 10; i++) {
            int data = i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        queue.enqueue(data);
                    } catch (InterruptedException e) {

                    }
                }
            }).start();
        }
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Integer data = queue.dequeue();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }
}

代碼非常容易理解,創建了一個阻塞隊列,大小為3,隊列滿的時候,會被阻塞,等待其他線程去消費,隊列中的元素被消費之后,會喚醒生產者,生產數據進入隊列。上面代碼將隊列大小置為1,可以實現同步阻塞隊列,生產1個元素之后,生產者會被阻塞,待消費者消費隊列中的元素之后,生產者才能繼續工作。

Object的監視器方法與Condition接口的對比

對比項 Object 監視器方法 Condition
前置條件 獲取對象的鎖 調用Lock.lock獲取鎖,調用Lock.newCondition()獲取Condition對象
調用方式 直接調用,如:object.wait() 直接調用,如:condition.await()
等待隊列個數 一個 多個,使用多個condition實現
當前線程釋放鎖并進入等待狀態 支持 支持
當前線程釋放鎖進入等待狀態中不響應中斷 不支持 支持
當前線程釋放鎖并進入超時等待狀態 支持 支持
當前線程釋放鎖并進入等待狀態到將來某個時間 不支持 支持
喚醒等待隊列中的一個線程 支持 支持
喚醒等待隊列中的全部線程 支持 支持

總結

  1. 使用condition的步驟:創建condition對象,獲取鎖,然后調用condition的方法
  2. 一個ReentrantLock支持床多個condition對象
  3. void await() throws InterruptedException;方法會釋放鎖,讓當前線程等待,支持喚醒,支持線程中斷
  4. void awaitUninterruptibly();方法會釋放鎖,讓當前線程等待,支持喚醒,不支持線程中斷
  5. long awaitNanos(long nanosTimeout) throws InterruptedException;參數為納秒,此方法會釋放鎖,讓當前線程等待,支持喚醒,支持中斷。超時之后返回的,結果為負數;超時之前返回的,結果為正數(表示返回時距離超時時間相差的納秒數)
  6. boolean await(long time, TimeUnit unit) throws InterruptedException;方法會釋放鎖,讓當前線程等待,支持喚醒,支持中斷。超時之后返回的,結果為false;超時之前返回的,結果為true
  7. boolean awaitUntil(Date deadline) throws InterruptedException;參數表示超時的截止時間點,方法會釋放鎖,讓當前線程等待,支持喚醒,支持中斷。超時之后返回的,結果為false;超時之前返回的,結果為true
  8. void signal();會喚醒一個等待中的線程,然后被喚醒的線程會被加入同步隊列,去嘗試獲取鎖
  9. void signalAll();會喚醒所有等待中的線程,將所有等待中的線程加入同步隊列,然后去嘗試獲取鎖

java高并發系列連載中,總計估計會有四五十篇文章,可以關注公眾號:javacode2018,獲取最新文章。

java高并發系列 - 第13天:JUC中的Condition對象

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

广河县| 保德县| 南投市| 泰来县| 松滋市| 垫江县| 潮州市| 辉县市| 松原市| 天祝| 治县。| 奉新县| 锡林郭勒盟| 望谟县| 安福县| 新闻| 屯昌县| 普宁市| 尼勒克县| 汕头市| 瑞丽市| 宿州市| 绵竹市| 镇原县| 林周县| 平利县| 巴里| 郎溪县| 历史| 公安县| 洪雅县| 永仁县| 浦北县| 二连浩特市| 东乡族自治县| 瑞金市| 新郑市| 广宁县| 海丰县| 白银市| 田林县|