最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

關(guān)于Java并發(fā)編程中線程間協(xié)作的兩種方式

 更新時(shí)間:2023年07月24日 09:16:04   作者:Matrix海 子  
這篇文章主要介紹了關(guān)于Java并發(fā)編程中線程間協(xié)作的兩種方式,當(dāng)隊(duì)列滿時(shí),生產(chǎn)者需要等待隊(duì)列有空間才能繼續(xù)往里面放入商品,而在等待的期間內(nèi),生產(chǎn)者必須釋放對(duì)臨界資源的占用權(quán),這是消費(fèi)者模式,需要的朋友可以參考下

引言

在前面我們將了很多關(guān)于同步的問題,然而在現(xiàn)實(shí)中,需要線程之間的協(xié)作。

比如說最經(jīng)典的生產(chǎn)者-消費(fèi)者模型:當(dāng)隊(duì)列滿時(shí),生產(chǎn)者需要等待隊(duì)列有空間才能繼續(xù)往里面放入商品,而在等待的期間內(nèi),生產(chǎn)者必須釋放對(duì)臨界資源(即隊(duì)列)的占用權(quán)。

因?yàn)樯a(chǎn)者如果不釋放對(duì)臨界資源的占用權(quán),那么消費(fèi)者就無法消費(fèi)隊(duì)列中的商品,就不會(huì)讓隊(duì)列有空間,那么生產(chǎn)者就會(huì)一直無限等待下去。

因此,一般情況下,當(dāng)隊(duì)列滿時(shí),會(huì)讓生產(chǎn)者交出對(duì)臨界資源的占用權(quán),并進(jìn)入掛起狀態(tài)。然后等待消費(fèi)者消費(fèi)了商品,然后消費(fèi)者通知生產(chǎn)者隊(duì)列有空間了。

同樣地,當(dāng)隊(duì)列空時(shí),消費(fèi)者也必須等待,等待生產(chǎn)者通知它隊(duì)列中有商品了。這種互相通信的過程就是線程間的協(xié)作。

今天我們就來探討一下Java中線程協(xié)作的最常見的兩種方式:利用Object.wait()、Object.notify()和使用Condition

一.wait()、notify()和notifyAll()

wait()、notify()和notifyAll()是Object類中的方法:

/**
 * Wakes up a single thread that is waiting on this object's
 * monitor. If any threads are waiting on this object, one of them
 * is chosen to be awakened. The choice is arbitrary and occurs at
 * the discretion of the implementation. A thread waits on an object's
 * monitor by calling one of the wait methods
 */
public final native void notify();
/**
 * Wakes up all threads that are waiting on this object's monitor. A
 * thread waits on an object's monitor by calling one of the
 * wait methods.
 */
public final native void notifyAll();
/**
 * Causes the current thread to wait until either another thread invokes the
 * {@link java.lang.Object#notify()} method or the
 * {@link java.lang.Object#notifyAll()} method for this object, or a
 * specified amount of time has elapsed.
 * <p>
 * The current thread must own this object's monitor.
 */
public final native void wait(long timeout) throws InterruptedException;

從這三個(gè)方法的文字描述可以知道以下幾點(diǎn)信息:

1)wait()、notify()和notifyAll()方法是本地方法,并且為final方法,無法被重寫。

2)調(diào)用某個(gè)對(duì)象的wait()方法能讓當(dāng)前線程阻塞,并且當(dāng)前線程必須擁有此對(duì)象的monitor(即鎖)

3)調(diào)用某個(gè)對(duì)象的notify()方法能夠喚醒一個(gè)正在等待這個(gè)對(duì)象的monitor的線程,如果有多個(gè)線程都在等待這個(gè)對(duì)象的monitor,則只能喚醒其中一個(gè)線程;

4)調(diào)用notifyAll()方法能夠喚醒所有正在等待這個(gè)對(duì)象的monitor的線程;

有朋友可能會(huì)有疑問:為何這三個(gè)不是Thread類聲明中的方法,而是Object類中聲明的方法(當(dāng)然由于Thread類繼承了Object類,所以Thread也可以調(diào)用者三個(gè)方法)?

其實(shí)這個(gè)問題很簡單,由于每個(gè)對(duì)象都擁有monitor(即鎖),所以讓當(dāng)前線程等待某個(gè)對(duì)象的鎖,當(dāng)然應(yīng)該通過這個(gè)對(duì)象來操作了。

而不是用當(dāng)前線程來操作,因?yàn)楫?dāng)前線程可能會(huì)等待多個(gè)線程的鎖,如果通過線程來操作,就非常復(fù)雜了。

上面已經(jīng)提到,如果調(diào)用某個(gè)對(duì)象的wait()方法,當(dāng)前線程必須擁有這個(gè)對(duì)象的monitor(即鎖),因此調(diào)用wait()方法必須在同步塊或者同步方法中進(jìn)行(synchronized塊或者synchronized方法)。

調(diào)用某個(gè)對(duì)象的wait()方法,相當(dāng)于讓當(dāng)前線程交出此對(duì)象的monitor,然后進(jìn)入等待狀態(tài),等待后續(xù)再次獲得此對(duì)象的鎖(Thread類中的sleep方法使當(dāng)前線程暫停執(zhí)行一段時(shí)間,從而讓其他線程有機(jī)會(huì)繼續(xù)執(zhí)行,但它并不釋放對(duì)象鎖);

notify()方法能夠喚醒一個(gè)正在等待該對(duì)象的monitor的線程,當(dāng)有多個(gè)線程都在等待該對(duì)象的monitor的話,則只能喚醒其中一個(gè)線程,具體喚醒哪個(gè)線程則不得而知。

同樣地,調(diào)用某個(gè)對(duì)象的notify()方法,當(dāng)前線程也必須擁有這個(gè)對(duì)象的monitor,因此調(diào)用notify()方法必須在同步塊或者同步方法中進(jìn)行(synchronized塊或者synchronized方法)。

nofityAll()方法能夠喚醒所有正在等待該對(duì)象的monitor的線程,這一點(diǎn)與notify()方法是不同的。

這里要注意一點(diǎn):notify()和notifyAll()方法只是喚醒等待該對(duì)象的monitor的線程,并不決定哪個(gè)線程能夠獲取到monitor。

舉個(gè)簡單的例子:假如有三個(gè)線程Thread1、Thread2和Thread3都在等待對(duì)象objectA的monitor,此時(shí)Thread4擁有對(duì)象objectA的monitor,當(dāng)在Thread4中調(diào)用objectA.notify()方法之后,Thread1、Thread2和Thread3只有一個(gè)能被喚醒。

注意,被喚醒不等于立刻就獲取了objectA的monitor。假若在Thread4中調(diào)用objectA.notifyAll()方法,則Thread1、Thread2和Thread3三個(gè)線程都會(huì)被喚醒,至于哪個(gè)線程接下來能夠獲取到objectA的monitor就具體依賴于操作系統(tǒng)的調(diào)度了。

上面尤其要注意一點(diǎn),一個(gè)線程被喚醒不代表立即獲取了對(duì)象的monitor,只有等調(diào)用完notify()或者notifyAll()并退出synchronized塊,釋放對(duì)象鎖后,其余線程才可獲得鎖執(zhí)行。

下面看一個(gè)例子就明白了:

public class Test {
    public static Object object = new Object();
    public static void main(String[] args) {
        Thread1 thread1 = new Thread1();
        Thread2 thread2 = new Thread2();
        thread1.start();
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread2.start();
    }
    static class Thread1 extends Thread{
        @Override
        public void run() {
            synchronized (object) {
                try {
                    object.wait();
                } catch (InterruptedException e) {
                }
                System.out.println("線程"+Thread.currentThread().getName()+"獲取到了鎖");
            }
        }
    }
    static class Thread2 extends Thread{
        @Override
        public void run() {
            synchronized (object) {
                object.notify();
                System.out.println("線程"+Thread.currentThread().getName()+"調(diào)用了object.notify()");
            }
            System.out.println("線程"+Thread.currentThread().getName()+"釋放了鎖");
        }
    }
}

無論運(yùn)行多少次,運(yùn)行結(jié)果必定是:

線程Thread-1調(diào)用了object.notify()
線程Thread-1釋放了鎖
線程Thread-0獲取到了鎖

二.Condition

Condition是在java 1.5中才出現(xiàn)的,它用來替代傳統(tǒng)的Object的wait()、notify()實(shí)現(xiàn)線程間的協(xié)作,相比使用Object的wait()、notify(),使用Condition1的await()、signal()這種方式實(shí)現(xiàn)線程間協(xié)作更加安全和高效。因此通常來說比較推薦使用Condition,在阻塞隊(duì)列那一篇博文中就講述到了,阻塞隊(duì)列實(shí)際上是使用了Condition來模擬線程間協(xié)作。

  • Condition是個(gè)接口,基本的方法就是await()和signal()方法;
  • Condition依賴于Lock接口,生成一個(gè)Condition的基本代碼是lock.newCondition()
  • 調(diào)用Condition的await()和signal()方法,都必須在lock保護(hù)之內(nèi),就是說必須在lock.lock()和lock.unlock之間才可以使用

Conditon中的await()對(duì)應(yīng)Object的wait();

Condition中的signal()對(duì)應(yīng)Object的notify();

Condition中的signalAll()對(duì)應(yīng)Object的notifyAll()。

三.生產(chǎn)者-消費(fèi)者模型的實(shí)現(xiàn)

1.使用Object的wait()和notify()實(shí)現(xiàn):

public class Test {
    private int queueSize = 10;
    private PriorityQueue<Integer> queue = new PriorityQueue<Integer>(queueSize);
    public static void main(String[] args)  {
        Test test = new Test();
        Producer producer = test.new Producer();
        Consumer consumer = test.new Consumer();
        producer.start();
        consumer.start();
    }
    class Consumer extends Thread{
        @Override
        public void run() {
            consume();
        }
        private void consume() {
            while(true){
                synchronized (queue) {
                    while(queue.size() == 0){
                        try {
                            System.out.println("隊(duì)列空,等待數(shù)據(jù)");
                            queue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            queue.notify();
                        }
                    }
                    queue.poll();          //每次移走隊(duì)首元素
                    queue.notify();
                    System.out.println("從隊(duì)列取走一個(gè)元素,隊(duì)列剩余"+queue.size()+"個(gè)元素");
                }
            }
        }
    }
    class Producer extends Thread{
        @Override
        public void run() {
            produce();
        }
        private void produce() {
            while(true){
                synchronized (queue) {
                    while(queue.size() == queueSize){
                        try {
                            System.out.println("隊(duì)列滿,等待有空余空間");
                            queue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            queue.notify();
                        }
                    }
                    queue.offer(1);        //每次插入一個(gè)元素
                    queue.notify();
                    System.out.println("向隊(duì)列取中插入一個(gè)元素,隊(duì)列剩余空間:"+(queueSize-queue.size()));
                }
            }
        }
    }
}

2.使用Condition實(shí)現(xiàn)

public class Test {
    private int queueSize = 10;
    private PriorityQueue<Integer> queue = new PriorityQueue<Integer>(queueSize);
    private Lock lock = new ReentrantLock();
    private Condition notFull = lock.newCondition();
    private Condition notEmpty = lock.newCondition();
    public static void main(String[] args)  {
        Test test = new Test();
        Producer producer = test.new Producer();
        Consumer consumer = test.new Consumer();
        producer.start();
        consumer.start();
    }
    class Consumer extends Thread{
        @Override
        public void run() {
            consume();
        }
        private void consume() {
            while(true){
                lock.lock();
                try {
                    while(queue.size() == 0){
                        try {
                            System.out.println("隊(duì)列空,等待數(shù)據(jù)");
                            notEmpty.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    queue.poll();                //每次移走隊(duì)首元素
                    notFull.signal();
                    System.out.println("從隊(duì)列取走一個(gè)元素,隊(duì)列剩余"+queue.size()+"個(gè)元素");
                } finally{
                    lock.unlock();
                }
            }
        }
    }
    class Producer extends Thread{
        @Override
        public void run() {
            produce();
        }
        private void produce() {
            while(true){
                lock.lock();
                try {
                    while(queue.size() == queueSize){
                        try {
                            System.out.println("隊(duì)列滿,等待有空余空間");
                            notFull.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    queue.offer(1);        //每次插入一個(gè)元素
                    notEmpty.signal();
                    System.out.println("向隊(duì)列取中插入一個(gè)元素,隊(duì)列剩余空間:"+(queueSize-queue.size()));
                } finally{
                    lock.unlock();
                }
            }
        }
    }
}

到此這篇關(guān)于關(guān)于Java并發(fā)編程中線程間協(xié)作的兩種方式的文章就介紹到這了,更多相關(guān)Java并發(fā)編程線程間協(xié)作內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • JDK1.8安裝與配置超詳細(xì)教程

    JDK1.8安裝與配置超詳細(xì)教程

    JDK1.8即為JDK8,JDK8是目前是最成熟最穩(wěn)定的版本,本文將詳細(xì)介紹JDK1.8的安裝與配置,需要的朋友可以參考下
    2023-03-03
  • Java流形式返回前端的實(shí)現(xiàn)示例

    Java流形式返回前端的實(shí)現(xiàn)示例

    ? Java后端開發(fā)項(xiàng)目時(shí),需要給前端傳一些數(shù)據(jù),本文主要介紹了Java流形式返回前端的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • Spring Security 在 Spring Boot 中的使用詳解【集中式】

    Spring Security 在 Spring Boot 中的使用詳解【集中式】

    這篇文章主要介紹了Spring Security 在 Spring Boot 中的使用【集中式】,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-10-10
  • Spring前后端跨域請(qǐng)求設(shè)置代碼實(shí)例

    Spring前后端跨域請(qǐng)求設(shè)置代碼實(shí)例

    這篇文章主要介紹了Spring前后端跨域請(qǐng)求設(shè)置代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-07-07
  • 通過Java實(shí)現(xiàn)獲取表的自增主鍵值

    通過Java實(shí)現(xiàn)獲取表的自增主鍵值

    這篇文章主要為大家詳細(xì)介紹了如何通過Java實(shí)現(xiàn)獲取表的自增主鍵值,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的可以了解一下
    2023-06-06
  • Java數(shù)組轉(zhuǎn)換為List的四種方式

    Java數(shù)組轉(zhuǎn)換為List的四種方式

    這篇文章主要介紹了Java開發(fā)技巧數(shù)組轉(zhuǎn)List的四種方式總結(jié),每種方式結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-09-09
  • Java使用Streams時(shí)的7個(gè)常見錯(cuò)誤與解決方案

    Java使用Streams時(shí)的7個(gè)常見錯(cuò)誤與解決方案

    這篇文章主要為大家詳細(xì)介紹了Java使用Streams時(shí)的7個(gè)常見錯(cuò)誤與解決方案,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2025-04-04
  • SpringBoot使用admin+actuator實(shí)現(xiàn)日志可視化的方法

    SpringBoot使用admin+actuator實(shí)現(xiàn)日志可視化的方法

    如何在SpringBoot中使用Admin和Actuator實(shí)現(xiàn)日志可視化,需配置pom依賴、yml參數(shù)及l(fā)ogback路徑,確保兩者一致,啟動(dòng)服務(wù)后通過指定端口訪問日志界面,并檢查404錯(cuò)誤原因,感興趣的朋友一起看看吧
    2025-06-06
  • springboot如何實(shí)現(xiàn)異步響應(yīng)請(qǐng)求(前端請(qǐng)求超時(shí)的問題解決)

    springboot如何實(shí)現(xiàn)異步響應(yīng)請(qǐng)求(前端請(qǐng)求超時(shí)的問題解決)

    這篇文章主要給大家介紹了關(guān)于springboot如何實(shí)現(xiàn)異步響應(yīng)請(qǐng)求(前端請(qǐng)求超時(shí)的問題解決)的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用springboot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2023-01-01
  • 淺談Java到底是值傳遞還是引用傳遞呢

    淺談Java到底是值傳遞還是引用傳遞呢

    今天帶大家學(xué)習(xí)Java的相關(guān)知識(shí),文章圍繞著Java到底是值傳遞還是引用傳遞展開,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06

最新評(píng)論

龙岩市| 恩施市| 张家港市| 邹平县| 新晃| 蕉岭县| 棋牌| 霍林郭勒市| 古丈县| 灵宝市| 乌鲁木齐县| 深水埗区| 阿荣旗| 汉沽区| 临邑县| 上饶市| 齐河县| 湘乡市| 聂拉木县| 宜兰市| 黄浦区| 枝江市| 汤原县| 财经| 塔城市| 霞浦县| 伊宁县| 合肥市| 阿坝县| 泸州市| 犍为县| 衡南县| 出国| 盐亭县| 通山县| 建德市| 册亨县| 平武县| 邢台市| 育儿| 恩施市|