黑馬程式員-學習日記(多安全執行緒問題和死結認識)

來源:互聯網
上載者:User

標籤:

------Java培訓、Android培訓、iOS培訓、期待與您交流! -------

安全問題產生的原因:

當多條代碼語句在操作同一個共用資料時,一個線程對多條語句只執行了一部分,還沒有執行完, 另一個線程參與進來執行。導致共用資料的錯誤。

class Mlpc implements Runnable{    private int ticket = 50;    public void run()     {        while(true)        {            if(ticket>0)            {                try { Thread.sleep(20); //讓線程進入凍結狀態 } catch (Exception e) { } System.out.println(Thread.currentThread()+" "+ticket--); } } }}class mlpcDemo { public static void main(String[] args) { Mlpc m = new Mlpc(); Thread th0 = new Thread(m); Thread th1 = new Thread(m); Thread th2 = new Thread(m); Thread th3 = new Thread(m); th0.start(); th1.start(); th2.start(); th3.start(); }}

出現了票數為-1和-2這樣的結果。

 解決辦法:

針對多條線程將會操作共用資料的那條語句,每次只能讓一個線程都執行完。在執行過程中,其他線程不可以參與執行。

方式:

同步代碼塊:

class Mlpc implements Runnable{    private int ticket = 50;    Object obj = new Object();    public void run()     {        while(true)        {            synchronized (obj) { if(ticket>0) { try { Thread.sleep(20); } catch (Exception e) { } System.out.println(Thread.currentThread()+" "+ticket--); } } } }}

對象如同鎖,持有鎖的線程可以在同步中執行。非持有鎖的線程,即使擷取cpu的執行權,也進不去,因為沒有擷取鎖。

同步的前提:

1.必須要有兩個或者兩個以上的線程

2.必須多個線程必須使用同一個鎖。 必須保證同步中只能有一個線程在運行。

好處:解決了線程的安全問題

弊端:消耗了資源,多個線程需要判斷鎖。

兩個儲戶分別向銀行存入300元,每次100,存3次。

找出改程式的安全問題。

1,明確哪些代碼是多線程運行代碼。

2,明確共用資料

3,明確多線程運行代碼中哪些語句是操作共用資料的。

我們建立4個線程,與之前不同的是,同時使用了同步函數和同步代碼快。看看結果如何。

class Mlpc implements Runnable{    private int ticket = 550;    public boolean flag = true;    Object obj = new Object();    public void run()     {        if(flag == true)        {            while(true)            {                synchronized (obj)                {                   if(ticket>0)                   {                          try                      {                         Thread.sleep(20);                      }                      catch (Exception e)                      {                      }                      System.out.println(Thread.currentThread()+" show "+ticket--);                    }                  }            }        }        else        {            while(true)            {                show();            }        }    }    public synchronized void show()    {            if(ticket>0)            {                    try                {                    Thread.sleep(20);                }                catch (Exception e)                {                }                System.out.println(Thread.currentThread()+" show "+ticket--);            }     }} class mlpcDemo {    public static void main(String[] args)     {             Mlpc m = new Mlpc();        Thread th0 = new Thread(m);        Thread th1 = new Thread(m);        Thread th2 = new Thread(m);        Thread th3 = new Thread(m);        th0.start();        try        {            Thread.sleep(20);            }        catch (Exception e)        {        }        m.flag = false;        th1.start();        th2.start();        th3.start();    }}

 結果出現了票數為0。

 public void run()     {        if(flag == true)        {            while(true)            {               synchronized (this) //從原本的任意對象改為函數自己所屬的對象this
{ if(ticket>0) { try { Thread.sleep(20); } catch (Exception e) { } System.out.println(Thread.currentThread()+" show "+ticket--); } } } } else { while(true) { show(); } } }

現在,無論如何都顯示正常結果。由此可以驗證出函數都有自己所屬的對象this,所以同步函數所使用的是this鎖。

死結:

當類中存在多個不同的鎖時,而這些鎖存在交叉,那麼就可能會出現死結

如:

1,鎖A--11

      {鎖B--22}

2,鎖B--33

      {鎖A--44} 

像上面這樣出現交叉的鎖。如11 33 同時執行後 要運行22 44就鎖住了。22被下面B鎖了,44又被上面11鎖住了。那麼就執行不下去了。 

class Demo implements Runnable{    private boolean flag;    Demo(boolean flag)    {        this.flag = flag;    }    public void run()     {        if(flag)        {            while(true)            synchronized(MyLock.locka)            {                System.out.println("if locka");                synchronized(MyLock.lockb)                {                    System.out.println("if lockb");                }            }        }        else        {            while (true)                        synchronized(MyLock.lockb)            {                System.out.println("else lockb");                synchronized(MyLock.locka)                {                    System.out.println("else locka");                }            }        }    }}class MyLock{    static Object locka = new Object();    static Object lockb = new Object();} class ThreadTest {    public static void main(String[] args)     {       Thread t1 = new Thread(new Demo(true));       Thread t2 = new Thread(new Demo(false));        t1.start();        try        {            Thread.sleep(100);        }        catch (Exception e)        {        }        t2.start();    }}

 

生產者與消費者問題:

 1 class PCDemo  2 { 3     public static void main(String[] args)  4     { 5         Resource r = new Resource(); 6         Producer prd = new Producer(r); 7 Consumer csu = new Consumer(r); 8 9 Thread th0 = new Thread(prd);10 Thread th1 = new Thread(prd); 11 Thread th2 = new Thread(prd);12 Thread th3 = new Thread(prd);13 14 Thread th4 = new Thread(csu);15 Thread th5 = new Thread(csu);16 Thread th6 = new Thread(csu);17 Thread th7 = new Thread(csu);18  th0.start();19  th1.start();20  th2.start();21  th3.start();22  th4.start();23  th5.start();24  th6.start();25  th7.start();26  }27 }28 29 30 class Resource31 {32 private String name;33 private int count =1;34 private boolean flag = false;35 36 public synchronized void set(String name)37  {38 if(flag)39 try40  {41  wait();42  }43 catch (Exception e)44  {45  }46 this.name = name+"---"+count++;47 System.out.println(Thread.currentThread().getName()+"生產"+this.name);48 flag = true;49 this.notify();50  }51 public synchronized void out()52  {53 if(!flag)54 try55  {56  wait();57  }58 catch (Exception e)59  {60  }61 System.out.println(Thread.currentThread().getName()+"________消費"+this.name);62 flag = false;63 this.notify();64  }65 66 }67 68 class Producer implements Runnable69 {70 private Resource res;71  Producer(Resource res)72  {73 this.res = res;74  }75 public void run()76  {77 while(true)78  {79 res.set("物件");80  }81  }82 }83 84 class Consumer implements Runnable85 {86 private Resource res;87  Consumer(Resource res)88  {89 this.res = res;90  }91 public void run()92  {93 while(true)94  {95  res.out();96  }97  }98 }

 如果只是開啟了兩個線程,運行時會輪流“消費--生產”很和諧。但如果兩者各開啟兩條線程,就會出現這樣的情況。

 t1取得資格、執行權->生產商品1、將flag之位true後t1進入阻塞狀態、t2取得資格和執行權->進入阻塞狀態,t3取得資格和執行權->消費商品1->喚醒第一個進入線程池的t1、t3而後阻塞、t1取得資格,若同樣取得資格t4搶到執行權->t4阻塞->t1取得執行權->生產商品2->喚醒t2、t2取得執行權後不再判斷flag真假直接生產t3。

於是將if判斷改為while迴圈,這樣的確彌補了錯誤。但會導致t2"醒來"->判斷flag真假->t2進入阻塞狀態。造成了所有線程全部等待。於是使用notifyAll();總是喚醒全部線程。又一次解決了問題。

JDK1.5提供了多線程升級解決方案,將同步synchronized替換成現實lock操作,要為特定 Lock 執行個體獲得 Condition 執行個體,使用其 newCondition() 方法。

Condition 執行個體實質上被綁定到一個鎖上。Condition 將 Object 監視器方法(wait、notify 和 notifyAll)分解成截然不同的對象,

以便通過將這些對象與任意 Lock 實現組合使用。

黑馬程式員-學習日記(多安全執行緒問題和死結認識)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.