十、Java語言基礎(chǔ)---多線程(同步涮总,等待喚醒機(jī)制胸囱,Lock,Condition)

這些是筆者學(xué)習(xí)Java基礎(chǔ)時的筆記,供后來者參考(學(xué)習(xí)是持之以恒的的事情瀑梗,一定要堅(jiān)持喲烹笔,切記!切記E桌觥0啊!)

1亿鲜、線程安全問題

問題描述:當(dāng)多個窗口(多個線程)同時售票允蜈,票為共享數(shù)據(jù)源;當(dāng)不對窗口(線程)進(jìn)行限制的時候,就容易造成線程安全問題饶套。

//下面的例子
public class Ticket implements Runnable {
  private static int num = 100;

    @Override
    public void run() {
      while(true){
         if(num>0){
          System.out.println(Thread.currentThread().getName()+"...sale..."+num);
          num--漩蟆;
        }else{
          break;
        }
      }
    }
}

public static void main(String[] args){
   Ticket ticket = new Ticket();
  Thread thread = new Thread(ticket);
  Thread thread1 = new Thread(ticket);
  Thread thread2 = new Thread(ticket);

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

此時,運(yùn)行代碼妓蛮,可能產(chǎn)生重票和跳票的問題怠李,這就是多個線程同時操作相同資源,造成的線程不安全問題蛤克。

2捺癞、解決線程安全問題(使用同步機(jī)制)

 (1)類鎖和對象鎖:
    1、類鎖:在代碼中的方法加static和synchronized的鎖咖耘,或者synchroonized(xxx.class)
    2翘簇、對象鎖:在代碼中的非靜態(tài)方法加了synchronized的鎖,或者synchronized(this)的代碼段儿倒;
    3版保、私有鎖:在類內(nèi)部聲明一個私有屬性如private Object lock,在需要加鎖的代碼段synchronized(lock)

(2)同步代碼塊:

    synchronized(鎖){ //鎖可以為任意對象夫否,但是需要保證多線程用的是同一把鎖
      對同一個資源的操作語句彻犁。
    }

(3)同步方法:
    <1>同步方法的鎖:this;

public class Ticket implements Runnable {
  private static int num = 100;

  @Override
  public void run() {
    while(true){
      synchronized (this){
        if(num > 0){
          System.out.println(Thread.currentThread().getName()+"...sale..."+num);
          num--;
        }else{
          break;
        }
      }
    }
  }
}

 <2>靜態(tài)同步方法的鎖:類名.class凰慈,使用synchronized修飾需要同步的方法汞幢;

public class Ticket implements Runnable {
  private static int num = 100;

  @Override
   public void run() {
     while(num>0){
       printTicket();
    }
  }

  private synchronized void printTicket(){
    if(num > 0){
      System.out.println(Thread.currentThread().getName()+"...sale..."+num);
      num--;
    }
  }
}

3、死鎖問題

    同步機(jī)制解決了線程安全問題微谓,同時也可能引入新的問題:線程死鎖問題森篷。
    問題描述:兩個線程操作同一個資源,但是在該資源上添加了兩個鎖A豺型,B构韵,當(dāng)其中一個線程獲取了鎖A联贩,同時另一個線程獲取了鎖B珍剑,這時就造成了線程死鎖缕溉。
    所以在平常開發(fā)過程中,為避免死鎖肴焊,就要盡量避免同步代碼塊的嵌套前联。

死鎖實(shí)例:
需求描述:用程序來描述以下情況:一手交錢一手交貨。商家與顧客兩人都是很小氣的人娶眷,顧客買商家的東西似嗤,商家收顧客的前,顧客說:先給我貨我再給你錢届宠;商家說:先給我錢我再給你貨烁落。最好的結(jié)局就是各自得到各自的東西壳咕。

代碼:
public class Customer extends Thread {
  public static Object money = new Object();

   @Override
    public void run() {
      synchronized (money){
        System.out.println("客戶等商家給貨");
        synchronized (Seller.goods){
        System.out.println("客戶給商家錢");
      }
    }
  }
}

public class Seller extends Thread {
  public static Object goods = new Object();

  @Override
  public void run() {
    synchronized (goods){
      System.out.println("商家等客戶給錢");
      synchronized (Customer.money){
        System.out.println("商家給客戶活");
      }
    }
  }
}

public class DeadLockDemo {
  public static void main(String[] args){
    Thread t1 = new Customer();
    Thread t2 = new Seller();
    t1.start();
    t2.start();
  }
}

4、等待喚醒機(jī)制
(1)當(dāng)多個線程執(zhí)行相同的任務(wù)顽馋,操作相同在資源的時候谓厘,使用加鎖機(jī)制,同步線程沒有問題寸谜。但當(dāng)多線程的執(zhí)行任務(wù)不同時竟稳,加鎖機(jī)制就失效了;這時我們就使用等待喚醒機(jī)制熊痴,完成線程同步他爸。
(2)問題線程(兩個問題,一讀寫資源不同步果善,二诊笤、線程不是交替執(zhí)行(不是生產(chǎn)消費(fèi)模型)):

public class Resource {
  String name;
  String sex;
}

public class Input implements Runnable{
  Resource r;
  public Input(Resource r){
    this.r = r;
  }

  @Override
  public void run() {
    int x=0;
    while(true){
      if(x == 0){
        r.name = "張三";
        r.sex = "男";
      }else{
        r.name = "小紅";
        r.sex = "女";
      }
      x=(x+1)%2;
    }
  }
}

public class Output implements Runnable{

  Resource r;

  public Output(Resource r){
    this.r = r;
  }

  @Override
  public void run() {
    while(true){
      System.out.println("---------name:"+r.name+" sex:"+r.sex);
    }
  }
}

public class ThreadDemo2 {

  public static void main(String[] args){
    Resource resource = new Resource();
    Thread thread = new Thread(new Input(resource));
    Thread thread1 = new Thread(new Output(resource));
    thread.start();
    thread1.start();
  }
}

(3)等待喚醒機(jī)制(必須在相同的鎖中,等待喚醒機(jī)制才能有效)
  wait():放棄線程執(zhí)行權(quán)巾陕,線程進(jìn)入線程池
  notify():喚醒線程池中任意一個線程
  notifyAll():將線程池中所有線程都喚醒

優(yōu)化后的代碼:
public class Resource {
  String name;
  String sex;
  boolean flag;

  public synchronized void set(String name,String sex){
     if(flag){
      try {
        this.wait();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
    this.name = name;
    this.sex = sex;
    flag = true;
    this.notify();
  }

  public synchronized void print(){
    if(!flag){
      try {
        this.wait();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }

    System.out.println("---------name:"+name+" sex:"+sex);
    flag = false;
    this.notify();
  }
}

public class Input implements Runnable{

  Resource r;

  public Input(Resource r){
    this.r = r;
  }

   @Override
   public void run() {
      int x=0;
      while(true){
        if(x == 0){
            r.name = "張三";
            r.sex = "男";
        }else{
            r.name = "小紅";
            r.sex = "女";
        }
        x=(x+1)%2;
    }
  }
}

public class Output implements Runnable{
  Resource r;

  public Output(Resource r){
    this.r = r;
  }

  @Override
  public void run() {
    while(true){
      r.print();
    }
  }
}

(4)多生產(chǎn)多消費(fèi)者的問題
    <1>當(dāng)前等待線程被喚醒的時候讨跟,沒有進(jìn)行標(biāo)記的判斷,所以會多生產(chǎn)和喚醒本方線程(生產(chǎn)者喚醒生產(chǎn)者鄙煤,消費(fèi)者
喚醒消費(fèi)者)的可能晾匠。為了解決這個問題,我們將條件判斷梯刚,換成while循環(huán)凉馆,將notify替換為notifyAll(),問題解決。

(5)線程的結(jié)束
    <1>線程的stop方法已經(jīng)過時亡资,結(jié)束線程的一種方法是澜共,將循環(huán)的標(biāo)記設(shè)置為false;線程自動結(jié)束锥腻。
    <2>當(dāng)線程的標(biāo)記讀不到時(即線程處于凍結(jié)狀態(tài)的時候)嗦董,調(diào)用interrupt()函數(shù)喚醒處于wait()、sleep()等凍結(jié)狀態(tài)
的線程旷太,讓其去讀取標(biāo)記展懈,結(jié)束線程销睁。

(6)setDaemon()守護(hù)線程
  該方法必須在start方法調(diào)用之前調(diào)用供璧。將參數(shù)設(shè)置為true時,線程就變成了守護(hù)線程冻记,被守護(hù)線程結(jié)束睡毒,守護(hù)線程
  自動結(jié)束。

(7)join()方法
  當(dāng)某個線程調(diào)用改方法的時候冗栗,改線程可以搶奪其他線程的執(zhí)行權(quán)演顾;被中段的線程需要等待程A執(zhí)行終止完成供搀,才被喚醒。
(通俗一點(diǎn)說钠至,join方法是阻塞的調(diào)用該方法的線程,當(dāng)被調(diào)用線程結(jié)束之后執(zhí)行)葛虐。

(8)setPriority() 設(shè)置線程的優(yōu)先級(取值范圍1~10)

(9)yield(),臨時釋放執(zhí)行權(quán)棉钧。

(10)wait()方法和sleep()方法的區(qū)別:
  <1>兩個方法可以讓線程處于凍結(jié)狀態(tài)屿脐;
  <2>sleep()必須指定時間,wait()方法可以指定時間宪卿,也可以不指定時間的诵;
  <3>sleep()會釋放執(zhí)行權(quán),不會釋放鎖佑钾。wait()會釋放執(zhí)行權(quán)西疤,同時也會釋放鎖;

(11)多生產(chǎn)者等待喚醒新寫法:
public class Product {
  private int number=0; //產(chǎn)品編號
  private boolean flag; //等待喚醒標(biāo)簽
  private Lock lock = new ReentrantLock();
  private Condition proCon = lock.newCondition();
  private Condition cusCon = lock.newCondition();

  public void get(){
    lock.lock();
    try{
      while (flag){
        try {
          proCon.await();
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
       }
      number++;
      System.out.println("生產(chǎn)者:"+Thread.currentThread()+"..生產(chǎn)產(chǎn)品:"+number);
      flag = true;
      cusCon.signal();
      }finally{
        lock.unlock();
      }
    }

  public void product(){
    lock.lock();
    try{
      while (!flag){
        try {
          cusCon.await();
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    System.out.println("消費(fèi)者:"+Thread.currentThread()+"..消費(fèi)產(chǎn)品:"+number);
    flag = false;
    proCon.signal();
  }finally{
    lock.unlock();
  }
  }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末休溶,一起剝皮案震驚了整個濱河市代赁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌兽掰,老刑警劉巖管跺,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異禾进,居然都是意外死亡豁跑,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門泻云,熙熙樓的掌柜王于貴愁眉苦臉地迎上來艇拍,“玉大人,你說我怎么就攤上這事宠纯⌒断Γ” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵婆瓜,是天一觀的道長快集。 經(jīng)常有香客問我,道長廉白,這世上最難降的妖魔是什么个初? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮猴蹂,結(jié)果婚禮上院溺,老公的妹妹穿的比我還像新娘。我一直安慰自己磅轻,他們只是感情好珍逸,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布逐虚。 她就那樣靜靜地躺著,像睡著了一般谆膳。 火紅的嫁衣襯著肌膚如雪叭爱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天漱病,我揣著相機(jī)與錄音涤伐,去河邊找鬼。 笑死缨称,一個胖子當(dāng)著我的面吹牛凝果,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播睦尽,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼器净,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了当凡?” 一聲冷哼從身側(cè)響起山害,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎沿量,沒想到半個月后浪慌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡朴则,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年权纤,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片乌妒。...
    茶點(diǎn)故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡汹想,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出撤蚊,到底是詐尸還是另有隱情古掏,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布侦啸,位于F島的核電站槽唾,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏光涂。R本人自食惡果不足惜庞萍,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望顶捷。 院中可真熱鬧挂绰,春花似錦屎篱、人聲如沸服赎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽重虑。三九已至践付,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間缺厉,已是汗流浹背永高。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留提针,地道東北人命爬。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像辐脖,于是被迫代替她去往敵國和親饲宛。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容