亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java多線程通信問題深入了解

 更新時(shí)間:2021年07月29日 11:46:52   作者:入錯(cuò)行的北北  
下面小編就為大家?guī)硪黄钊肜斫釰AVA多線程之線程間的通信方式。小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧

概述

多線程通信問題,也就是生產(chǎn)者與消費(fèi)者問題

生產(chǎn)者和消費(fèi)者為兩個(gè)線程,兩個(gè)線程在運(yùn)行過程中交替睡眠,生產(chǎn)者在生產(chǎn)時(shí)消費(fèi)者沒有在消費(fèi),消費(fèi)者在消費(fèi)時(shí)生產(chǎn)者沒有在生產(chǎn),確保數(shù)據(jù)安全

以下為百度百科對(duì)于該問題的解釋:

生產(chǎn)者與消費(fèi)者問題:
生產(chǎn)者消費(fèi)者問題(Producer-consumer problem),也稱有限緩沖問題(Bounded-buffer problem),是一個(gè)多線程同步問題的經(jīng)典案例。該問題描述了兩個(gè)共享固定大小緩沖區(qū)的線程——即所謂的“生產(chǎn)者”和“消費(fèi)者”——在實(shí)際運(yùn)行時(shí)會(huì)發(fā)生的問題。生產(chǎn)者的主要作用是生成一定量的數(shù)據(jù)放到緩沖區(qū)中,然后重復(fù)此過程。與此同時(shí),消費(fèi)者也在緩沖區(qū)消耗這些數(shù)據(jù)。該問題的關(guān)鍵就是要保證生產(chǎn)者不會(huì)在緩沖區(qū)滿時(shí)加入數(shù)據(jù),消費(fèi)者也不會(huì)在緩沖區(qū)中空時(shí)消耗數(shù)據(jù)。

解決辦法:
要解決該問題,就必須讓生產(chǎn)者在緩沖區(qū)滿時(shí)休眠(要么干脆就放棄數(shù)據(jù)),等到下次消費(fèi)者消耗緩沖區(qū)中的數(shù)據(jù)的時(shí)候,生產(chǎn)者才能被喚醒,開始往緩沖區(qū)添加數(shù)據(jù)。同樣,也可以讓消費(fèi)者在緩沖區(qū)空時(shí)進(jìn)入休眠,等到生產(chǎn)者往緩沖區(qū)添加數(shù)據(jù)之后,再喚醒消費(fèi)者。通常采用進(jìn)程間通信的方法解決該問題,常用的方法有信號(hào)燈法等。如果解決方法不夠完善,則容易出現(xiàn)死鎖的情況。出現(xiàn)死鎖時(shí),兩個(gè)線程都會(huì)陷入休眠,等待對(duì)方喚醒自己。該問題也能被推廣到多個(gè)生產(chǎn)者和消費(fèi)者的情形。

引入

該過程可以類比為一個(gè)栗子:

廚師為生產(chǎn)者,服務(wù)員為消費(fèi)者,假設(shè)只有一個(gè)盤子盛放食品。

廚師在生產(chǎn)食品(廚師線程運(yùn)行)的過程中,服務(wù)員應(yīng)當(dāng)?shù)却ǚ?wù)員線程睡眠),等到食品生產(chǎn)完成(廚師線程結(jié)束)后將食品放入盤子中,服務(wù)員將盤子端出去(服務(wù)員線程運(yùn)行),此時(shí)沒有盤子可以放食品,因此廚師休息(廚師線程休眠),一段時(shí)間過后服務(wù)員將盤子拿回來(服務(wù)員線程結(jié)束),廚師開始進(jìn)行生產(chǎn)食品(廚師線程運(yùn)行),服務(wù)員在一旁等待(服務(wù)員線程睡眠)…

在此過程中,廚師和服務(wù)員兩個(gè)線程交替睡眠,廚師在做飯時(shí)服務(wù)員沒有端盤子(廚師線程運(yùn)行時(shí)服務(wù)員線程睡眠),服務(wù)員在端盤子時(shí)廚師沒有在做飯(服務(wù)員線程運(yùn)行時(shí)廚師線程睡眠),確保了數(shù)據(jù)的安全

根據(jù)廚師和服務(wù)員這個(gè)栗子,我們可以通過代碼來一步步實(shí)現(xiàn)

  • 定義廚師線程
 /**
     * 廚師,是一個(gè)線程
     */
    static class Cook extends Thread{
        private Food f;
        public Cook(Food f){
            this.f = f;
        }
        //運(yùn)行的線程,生成100道菜
        @Override
        public void run() {
            for (int i = 0 ; i < 100; i ++){
                if(i % 2 == 0){
                    f.setNameAneTaste("小米粥","沒味道,不好吃");
                }else{
                    f.setNameAneTaste("老北京雞肉卷","甜辣味");
                }
            }
        }
    }
  • 定義服務(wù)員線程
/**
     * 服務(wù)員,是一個(gè)線程
     */
    static class Waiter extends Thread{
        private Food f;
        public Waiter(Food f){
            this.f = f;
        }
        @Override
        public void run() {
            for(int i =0 ; i < 100;i ++){
                //等待
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                f.get();
            }
        }//end run
    }//end waiter
  • 新建食物類
 /**
     * 食物,對(duì)象
     */
    static class Food{
        private String name;
        private String taste;
        public void setNameAneTaste(String name,String taste){
            this.name = name;
            //加了這段之后,有可能這個(gè)地方的時(shí)間片更有可能被搶走,從而執(zhí)行不了this.taste = taste
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.taste = taste;
        }//end set
        public void get(){
            System.out.println("服務(wù)員端走的菜的名稱是:" + this.name + " 味道:" + this.taste);
        }
    }//end food

main方法中去調(diào)用兩個(gè)線程

    public static void main(String[] args) {
        Food f = new Food();
        Cook c = new Cook(f);
        Waiter w = new Waiter(f);
        c.start();//廚師線程
        w.start();//服務(wù)生線程     
    }

運(yùn)行結(jié)果:

只截取了一部分,我們可以看到,“小米粥”并沒有每次都對(duì)應(yīng)“沒味道,不好吃”,“老北京雞肉卷”也沒有每次都對(duì)應(yīng)“甜辣味”,而是一種錯(cuò)亂的對(duì)應(yīng)關(guān)系

...
服務(wù)員端走的菜的名稱是:老北京雞肉卷 味道:沒味道,不好吃
服務(wù)員端走的菜的名稱是:小米粥 味道:甜辣味
服務(wù)員端走的菜的名稱是:老北京雞肉卷 味道:沒味道,不好吃
服務(wù)員端走的菜的名稱是:小米粥 味道:甜辣味
服務(wù)員端走的菜的名稱是:老北京雞肉卷 味道:沒味道,不好吃
服務(wù)員端走的菜的名稱是:小米粥 味道:甜辣味
服務(wù)員端走的菜的名稱是:老北京雞肉卷 味道:沒味道,不好吃
服務(wù)員端走的菜的名稱是:小米粥 味道:甜辣味
服務(wù)員端走的菜的名稱是:老北京雞肉卷 味道:沒味道,不好吃
服務(wù)員端走的菜的名稱是:老北京雞肉卷 味道:甜辣味
...

name和taste對(duì)應(yīng)錯(cuò)亂的原因:

當(dāng)廚師調(diào)用set方法時(shí),剛設(shè)置完name,程序進(jìn)行了休眠,此時(shí)服務(wù)員可能已經(jīng)將食品端走了,而此時(shí)的taste是上一次運(yùn)行時(shí)保留的taste。

兩個(gè)線程一起運(yùn)行時(shí),由于使用搶占式調(diào)度模式,沒有協(xié)調(diào),因此出現(xiàn)了該現(xiàn)象

以上運(yùn)行結(jié)果解釋如圖:

在這里插入圖片描述

加入線程安全

針對(duì)上面的線程不安全問題,對(duì)廚師set和服務(wù)員get這兩個(gè)線程都使用synchronized關(guān)鍵字,實(shí)現(xiàn)線程安全,即:當(dāng)一個(gè)線程正在執(zhí)行時(shí),另外的線程不會(huì)執(zhí)行,在后面排隊(duì)等待當(dāng)前的程序執(zhí)行完后再執(zhí)行

代碼如下所示,分別給兩個(gè)方法添加synchronized修飾符,以方法為單位進(jìn)行加鎖,實(shí)現(xiàn)線程安全

	/**
     * 食物,對(duì)象
     */
    static class Food{
        private String name;
        private String taste;
        public synchronized void setNameAneTaste(String name,String taste){
            this.name = name;
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.taste = taste;
        }//end set
        public synchronized void get(){
            System.out.println("服務(wù)員端走的菜的名稱是:" + this.name + " 味道:" + this.taste);
        }
    }//end food

輸出結(jié)果:

由輸出可見,又出現(xiàn)了新的問題:
雖然加入了線程安全,set和get方法不再像前面一樣同時(shí)執(zhí)行并且菜名和味道一一對(duì)應(yīng),但是set和get方法并沒有交替執(zhí)行(通俗地講,不是廚師一做完服務(wù)員就端走),而是無序地執(zhí)行(廚師有可能做完之后繼續(xù)做,做好幾道,服務(wù)員端好幾次…無規(guī)律地做和端)

...
服務(wù)員端走的菜的名稱是:小米粥 味道:沒味道,不好吃
服務(wù)員端走的菜的名稱是:小米粥 味道:沒味道,不好吃
服務(wù)員端走的菜的名稱是:老北京雞肉卷 味道:甜辣味
服務(wù)員端走的菜的名稱是:小米粥 味道:沒味道,不好吃
服務(wù)員端走的菜的名稱是:老北京雞肉卷 味道:甜辣味
服務(wù)員端走的菜的名稱是:小米粥 味道:沒味道,不好吃
服務(wù)員端走的菜的名稱是:老北京雞肉卷 味道:甜辣味
服務(wù)員端走的菜的名稱是:小米粥 味道:沒味道,不好吃
服務(wù)員端走的菜的名稱是:老北京雞肉卷 味道:甜辣味
服務(wù)員端走的菜的名稱是:小米粥 味道:沒味道,不好吃
服務(wù)員端走的菜的名稱是:小米粥 味道:沒味道,不好吃
服務(wù)員端走的菜的名稱是:老北京雞肉卷 味道:甜辣味
服務(wù)員端走的菜的名稱是:老北京雞肉卷 味道:甜辣味
服務(wù)員端走的菜的名稱是:小米粥 味道:沒味道,不好吃
服務(wù)員端走的菜的名稱是:老北京雞肉卷 味道:甜辣味
服務(wù)員端走的菜的名稱是:老北京雞肉卷 味道:甜辣味
服務(wù)員端走的菜的名稱是:老北京雞肉卷 味道:甜辣味
服務(wù)員端走的菜的名稱是:老北京雞肉卷 味道:甜辣味
服務(wù)員端走的菜的名稱是:老北京雞肉卷 味道:甜辣味
服務(wù)員端走的菜的名稱是:老北京雞肉卷 味道:甜辣味
...

實(shí)現(xiàn)生產(chǎn)者與消費(fèi)者問題

由上面可知,加入線程安全依舊無法實(shí)現(xiàn)該問題。因此,要解決該問題,回到前面的引入部分,嚴(yán)格按照生產(chǎn)者與消費(fèi)者問題中所說地去編寫程序

生產(chǎn)者與消費(fèi)者問題:
生產(chǎn)者和消費(fèi)者為兩個(gè)線程,兩個(gè)線程在運(yùn)行過程中交替睡眠,生產(chǎn)者在生產(chǎn)時(shí)消費(fèi)者沒有在消費(fèi),消費(fèi)者在消費(fèi)時(shí)生產(chǎn)者沒有在生產(chǎn),確保數(shù)據(jù)安全

廚師在生產(chǎn)食品(廚師線程運(yùn)行)的過程中,服務(wù)員應(yīng)當(dāng)?shù)却ǚ?wù)員線程睡眠),等到食品生產(chǎn)完成(廚師線程結(jié)束)后將食品放入盤子中,服務(wù)員將盤子端出去(服務(wù)員線程運(yùn)行),此時(shí)沒有盤子可以放食品,因此廚師休息(廚師線程休眠),一段時(shí)間過后服務(wù)員將盤子拿回來(服務(wù)員線程結(jié)束),廚師開始進(jìn)行生產(chǎn)食品(廚師線程運(yùn)行),服務(wù)員在一旁等待(服務(wù)員線程睡眠)…

在此過程中,廚師和服務(wù)員兩個(gè)線程交替睡眠,廚師在做飯時(shí)服務(wù)員沒有端盤子(廚師線程運(yùn)行時(shí)服務(wù)員線程睡眠),服務(wù)員在端盤子時(shí)廚師沒有在做飯(服務(wù)員線程運(yùn)行時(shí)廚師線程睡眠),確保數(shù)據(jù)的安全

需要用到的java.lang.Object 中的方法:

變量和類型 方法 描述
void notify() 喚醒當(dāng)前this下的單個(gè)線程
void notifyAll() 喚醒當(dāng)前this下的所有線程
void wait() 當(dāng)前線程休眠
void wait​(long timeoutMillis) 當(dāng)前線程休眠一段時(shí)間
void wait​(long timeoutMillis, int nanos) 當(dāng)前線程休眠一段時(shí)間
  • 首先在Food類中加一個(gè)標(biāo)記flag:

True表示廚師生產(chǎn),服務(wù)員休眠

False表示服務(wù)員端菜,廚師休眠

private boolean flag = true;

對(duì)set方法進(jìn)行修改

當(dāng)且僅當(dāng)flag為True(True表示廚師生產(chǎn),服務(wù)員休眠)時(shí),才能進(jìn)行做菜操作

做菜結(jié)束時(shí),將flag置為False(False表示服務(wù)員端菜,廚師休眠),這樣廚師在生產(chǎn)完之后不會(huì)繼續(xù)生產(chǎn),避免了廚師兩次生產(chǎn)、服務(wù)員端走一份的情況

然后喚醒在當(dāng)前this下休眠的所有進(jìn)程,而廚師線程進(jìn)行休眠

		public synchronized void setNameAneTaste(String name,String taste){
            if(flag){//當(dāng)標(biāo)記為true時(shí),表示廚師可以生產(chǎn),該方法才執(zhí)行
                this.name = name;
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.taste = taste;
                flag = false;//生產(chǎn)完之后,標(biāo)記置為false,這樣廚師在生產(chǎn)完之后不會(huì)繼續(xù)生產(chǎn),避免了廚師兩次生產(chǎn)、服務(wù)員端走一份的情況
                this.notifyAll();//喚醒在當(dāng)前this下休眠的所有進(jìn)程
                try {
                    this.wait();//此時(shí)廚師線程進(jìn)行休眠
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }//end set
  • 對(duì)get方法進(jìn)行修改

當(dāng)且僅當(dāng)flag為False(False表示服務(wù)員端菜,廚師休眠)時(shí),才能進(jìn)行端菜操作

端菜結(jié)束時(shí),將flag置為True(True表示廚師生產(chǎn),服務(wù)員休眠),這樣服務(wù)員在端完菜之后不會(huì)繼續(xù)端菜,避免了服務(wù)員兩次端菜、廚師生產(chǎn)一份的情況

然后喚醒在當(dāng)前this下休眠的所有進(jìn)程,而服務(wù)員線程進(jìn)行休眠

        public synchronized void get(){
            if(!flag){//廚師休眠的時(shí)候,服務(wù)員開始端菜
                System.out.println("服務(wù)員端走的菜的名稱是:" + this.name + " 味道:" + this.taste);
                flag = true;//端完之后,標(biāo)記置為true,這樣服務(wù)員在端完菜之后不會(huì)繼續(xù)端菜,避免了服務(wù)員兩次端菜、廚師只生產(chǎn)一份的情況
                this.notifyAll();//喚醒在當(dāng)前this下休眠的所有進(jìn)程
                try {
                    this.wait();//此時(shí)服務(wù)員線程進(jìn)行休眠
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }// end if
        }//end get

作了以上調(diào)整之后的程序輸出:

我們可以看到,沒有出現(xiàn)數(shù)據(jù)錯(cuò)亂,并且菜的順序是交替依次進(jìn)行的

...
服務(wù)員端走的菜的名稱是:小米粥 味道:沒味道,不好吃
服務(wù)員端走的菜的名稱是:老北京雞肉卷 味道:甜辣味
服務(wù)員端走的菜的名稱是:小米粥 味道:沒味道,不好吃
服務(wù)員端走的菜的名稱是:老北京雞肉卷 味道:甜辣味
服務(wù)員端走的菜的名稱是:小米粥 味道:沒味道,不好吃
服務(wù)員端走的菜的名稱是:老北京雞肉卷 味道:甜辣味
服務(wù)員端走的菜的名稱是:小米粥 味道:沒味道,不好吃
服務(wù)員端走的菜的名稱是:老北京雞肉卷 味道:甜辣味
服務(wù)員端走的菜的名稱是:小米粥 味道:沒味道,不好吃
服務(wù)員端走的菜的名稱是:老北京雞肉卷 味道:甜辣味
服務(wù)員端走的菜的名稱是:小米粥 味道:沒味道,不好吃
服務(wù)員端走的菜的名稱是:老北京雞肉卷 味道:甜辣味
...

這就是生產(chǎn)者與消費(fèi)者問題的一個(gè)典型例子

總結(jié)

本篇文章就到這里了,希望能給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!

相關(guān)文章

  • SpringBoot實(shí)現(xiàn)定時(shí)發(fā)送郵件的三種方法案例詳解

    SpringBoot實(shí)現(xiàn)定時(shí)發(fā)送郵件的三種方法案例詳解

    這篇文章主要介紹了SpringBoot三種方法實(shí)現(xiàn)定時(shí)發(fā)送郵件的案例,Spring框架的定時(shí)任務(wù)調(diào)度功能支持配置和注解兩種方式Spring?Boot在Spring框架的基礎(chǔ)上實(shí)現(xiàn)了繼承,并對(duì)其中基于注解方式的定時(shí)任務(wù)實(shí)現(xiàn)了非常好的支持,本文給大家詳細(xì)講解,需要的朋友可以參考下
    2023-03-03
  • 基于Java回顧之多線程詳解

    基于Java回顧之多線程詳解

    在這篇文章里,我們關(guān)注多線程。多線程是一個(gè)復(fù)雜的話題,包含了很多內(nèi)容,這篇文章主要關(guān)注線程的基本屬性、如何創(chuàng)建線程、線程的狀態(tài)切換以及線程通信,我們把線程同步的話題留到下一篇文章中
    2013-05-05
  • Java并發(fā)之嵌套管程鎖死詳解

    Java并發(fā)之嵌套管程鎖死詳解

    這篇文章主要介紹了Java并發(fā)之嵌套管程鎖死詳解,涉及嵌套管程鎖死的發(fā)生,實(shí)例等相關(guān)內(nèi)容,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-11-11
  • MyBatis高級(jí)映射學(xué)習(xí)教程

    MyBatis高級(jí)映射學(xué)習(xí)教程

    這篇文章主要介紹了MyBatis高級(jí)映射學(xué)習(xí)教程的相關(guān)資料,需要的朋友可以參考下
    2016-05-05
  • Java方法的覆蓋與隱藏的區(qū)別分析

    Java方法的覆蓋與隱藏的區(qū)別分析

    本篇文章介紹了,關(guān)于Java方法的覆蓋與隱藏的區(qū)別分析。需要的朋友參考下
    2013-04-04
  • Java8新特性之重復(fù)注解(repeating annotations)淺析

    Java8新特性之重復(fù)注解(repeating annotations)淺析

    這篇文章主要介紹了Java8新特性之重復(fù)注解(repeating annotations)淺析,這個(gè)新特性只是修改了程序的可讀性,是比較小的一個(gè)改動(dòng),需要的朋友可以參考下
    2014-06-06
  • 用java WebSocket做一個(gè)聊天室

    用java WebSocket做一個(gè)聊天室

    這篇文章主要為大家詳細(xì)介紹了用java WebSocket做一個(gè)聊天室,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-10-10
  • IDEA報(bào)java:?java.lang.OutOfMemoryError:?Java?heap?space錯(cuò)誤解決辦法

    IDEA報(bào)java:?java.lang.OutOfMemoryError:?Java?heap?space錯(cuò)誤

    這篇文章主要給大家介紹了關(guān)于IDEA報(bào)java:?java.lang.OutOfMemoryError:?Java?heap?space錯(cuò)誤的解決辦法,文中將解決的辦法介紹的非常詳細(xì),需要的朋友可以參考下
    2024-01-01
  • Spring中TransactionSynchronizationManager的使用詳解

    Spring中TransactionSynchronizationManager的使用詳解

    這篇文章主要介紹了Spring中TransactionSynchronizationManager的使用詳解,TransactionSynchronizationManager是事務(wù)同步管理器,監(jiān)聽事務(wù)的操作,來實(shí)現(xiàn)在事務(wù)前后可以添加一些指定操作,需要的朋友可以參考下
    2023-09-09
  • java寫入zip文件后無法進(jìn)行刪除的問題及解決

    java寫入zip文件后無法進(jìn)行刪除的問題及解決

    這篇文章主要介紹了java寫入zip文件后無法進(jìn)行刪除的問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-06-06

最新評(píng)論