多線程實現資源共享的問題學習與總結


我么知道Java傳統多線程的實現有兩種方法,繼承Thread類或者實現Runnable即可.線程啟動時調用start()方法.

實現Runnable接口相比繼承Thread類有如下好處:

1.避免單繼承的局限,一個類可以同時實現多個接口

2.適合資源的共享.

 

實現多線程模擬售票點賣票來說明實現Runnable即可可以達到資源共享的目的.

使用繼承Thread類的多線程售票實現

package org.dennist.thread.demo; /** * * TicketThread.java * * @version : 1.1 * * @author : 蘇若年 <a href="mailto:DennisIT@163.com">發送郵件</a> * * @since : 1.0 創建時間: 2013-2-24 下午02:22:49 * * TODO : class TicketThread.java is used for ... * */
public class TicketThreadT extends Thread{ private int num = 5;        //總共票數設定為5張
 @Override public void run() { for(int i=0; i<10; i++){ if(this.num>0){        //打印買票信息
                System.out.println(Thread.currentThread().getName() + "買票: " + this.num--); } } } public static void main(String[] args) { TicketThreadT th1 = new TicketThreadT();        //線程一
        th1.setName("售票口一"); TicketThreadT th2 = new TicketThreadT();        //線程二
        th2.setName("售票口二"); TicketThreadT th3 = new TicketThreadT();        //線程三
        th3.setName("售票口三"); //分別啟動三個線程
 th1.start(); th2.start(); th3.start(); } }

程序運行結果:

總共5張票,啟動了三個線程,從打印結果可以看出,一共賣出去了15張票,線程之間沒有進行資源共享

實現Runnable的售票線程

package org.dennist.thread.demo; /** * * TicketThreadR.java * * @version : 1.1 * * @author : 蘇若年 <a href="mailto:DennisIT@163.com">發送郵件</a> * * @since : 1.0 創建時間: 2013-2-24 下午02:29:23 * * TODO : class TicketThreadR.java is used for ... * */
public class TicketThreadR implements Runnable{ private int num = 5;            //總共票數設定為5張
 @Override public void run() { for(int i=0; i<10; i++){ if(this.num>0){            //打印買票信息
                System.out.println(Thread.currentThread().getName() + "買票: " + this.num--); } } } public static void main(String[] args) { TicketThreadR ticketThread = new TicketThreadR(); Thread th1 = new Thread(ticketThread);    //線程一
        th1.setName("售票口一"); Thread th2 = new Thread(ticketThread);    //線程二
        th2.setName("售票口二"); Thread th3 = new Thread(ticketThread);    //線程三
        th3.setName("售票口三"); th1.start(); th2.start(); th3.start(); } }

程序運行結果

雖然現在程序中有三個線程,但是三個線程總共賣出了5張票,也就是說使用Runnable實現的多線程可以達到資源共享的目的.

Java多線程訪問共享方式

(1)如果每個線程執行的代碼相同,可以使用同一個Runnable對象,這個Runnable對象中有那個共享數據,例如,買票系統就可以這么做。

(2)如果每個線程執行的代碼不同,這時候需要用不同的Runnable對象,有如下兩種方式來實現這些Runnable對象之間的數據共享:

  1、將共享數據封裝在另外一個對象中,然后將這個對象逐一傳遞給各個Runnable對象。每個線程對共享數據的操作方法也分配到那個對象身上去完成,這樣容易實現針對該數據進行的各個操作的互斥和通信。

  2、將這些Runnable對象作為某一個類中的內部類,共享數據作為這個外部類中的成員變量,每個線程對共享數據的操作方法也分配給外部類,以便實現對共享數據進行的各個操作的互斥和通信,作為內部類的各個Runnable對象調用外部類的這些方法。

  3、上面兩種方式的組合:將共享數據封裝在另外一個對象中,每個線程對共享數據的操作方法也分配到那個對象身上去完成,對象作為這個外部類中的成員變量或方法中的局部變量,每個線程的Runnable對象作為外部類中的成員內部類或局部內部類。

  4、總之,要同步互斥的幾段代碼最好是分別放在幾個獨立的方法中,這些方法再放在同一個類中,這樣比較容易實現它們之間的同步互斥和通信。

(3)極端且簡單的方式,即在任意一個類中定義一個static的變量,這將被所有線程共享。

 

 

Thread類中存在以下的幾個方法可以設置和取得名字.

設置名字: public final void setName(String name) 

public Thread(Runnable target, String name)

public Thread(String name)

取得名字: public final String getName()

在線程的操作中因為其操作的不確定性,所以提供了一個方法,可以取得當前的操作線程.

public static Thread currentThread()

說明:

對於線程的名字一般是在啟動前進行設置,最好不要設置相同的名字,最好不要為一個線程改名字.

Java執行中一個Java程序至少啟動2個線程:一個主線程和一個垃圾回收線程.

 

多線程的同步問題

上面的實現Runnable程序就真的沒問題了嗎?我們知道現實生活中買票總會有等待,跟延遲,那么我們模擬現實生活中的買票然后再來看上面的程序輸出.

package org.dennist.thread.demo; /** * * TicketThreadR.java * * @version : 1.1 * * @author : 蘇若年 <a href="mailto:DennisIT@163.com">發送郵件</a> * * @since : 1.0 創建時間: 2013-2-24 下午02:29:23 * * TODO : class TicketThreadR.java is used for ... * */
public class TicketThreadR implements Runnable{ private int num = 5;            //總共票數設定為5張
 @Override public void run() { for(int i=0; i<10; i++){ try { Thread.sleep(200);    //休息200毫秒
            } catch (InterruptedException e) { e.printStackTrace(); } if(this.num>0){            //打印買票信息
                System.out.println(Thread.currentThread().getName() + "買票: " + this.num--); } } } public static void main(String[] args) { TicketThreadR ticketThread = new TicketThreadR(); Thread th1 = new Thread(ticketThread);    //線程一
        th1.setName("售票口一"); Thread th2 = new Thread(ticketThread);    //線程二
        th2.setName("售票口二"); Thread th3 = new Thread(ticketThread);    //線程三
        th3.setName("售票口三"); th1.start(); th2.start(); th3.start(); } }

如果想解決這樣的問題,就必須使用同步,所謂的同步就是指多個操作在同一個時間段內只有一個線程進行,其他線程要等待此線程完成之后才可以繼續執行.

可以通過同步代碼的方法進行代碼的加鎖操作,同步的實現有2中方法:

JAVA多線程同步主要依賴於若干方法和關鍵字
  1  wait方法

  2  notify方法和notifyAll方法

  3  synchronized關鍵字

  4 atomic action(原子操作)

詳細說明見:[http://www.cnblogs.com/dennisit/archive/2013/02/25/2931573.html]

此處針對上面情況使用同步關鍵字synchronized解決.同步關鍵字使用有2種方法

  1.同步代碼塊

  2.同步方法

同步代碼塊

使用synchronized關鍵字進行同步代碼塊的聲明,但是在使用此操作時必須明確的指出到底要鎖定的是哪個對象,一般是以當前對象為主.

  synchronized(對象){   //一般都是講this鎖定

         //鎖定對象

     }

上面的問題使用同步代碼塊解決

package org.dennist.thread.demo; /** * * TicketThreadR.java * * @version : 1.1 * * @author : 蘇若年 <a href="mailto:DennisIT@163.com">發送郵件</a> * * @since : 1.0 創建時間: 2013-2-24 下午02:29:23 * * TODO : class TicketThreadR.java is used for ... * */
public class TicketThreadR implements Runnable{ private int num = 5;            //總共票數設定為5張
 @Override public void run() { for(int i=0; i<10; i++){ //使用同步代碼塊
            synchronized (this) { try { Thread.sleep(300);    //休息300毫秒
                } catch (InterruptedException e) { e.printStackTrace(); } if(this.num>0){ //打印買票信息
                    System.out.println(Thread.currentThread().getName() + "買票: " + this.num--); } } } } public static void main(String[] args) { TicketThreadR ticketThread = new TicketThreadR(); new Thread(ticketThread,"售票口一").start();    //線程一
        new Thread(ticketThread,"售票口二").start();    //線程二
        new Thread(ticketThread,"售票口三").start();    //線程三
 } }

 

同步方法

同步方法是在方法上增加synchronized關鍵字修飾
上面的問題使用同步代碼塊解決

package org.dennist.thread.demo; /** * * TicketThreadR.java * * @version : 1.1 * * @author : 蘇若年 <a href="mailto:DennisIT@163.com">發送郵件</a> * * @since : 1.0 創建時間: 2013-2-24 下午02:29:23 * * TODO : class TicketThreadR.java is used for ... * */
public class TicketThreadR implements Runnable{ private int num = 5;            //總共票數設定為5張
 @Override public void run() { for(int i=0; i<10; i++){ sale(); //調用同步方法
 } } //使用同步方法
    public synchronized void sale(){ try { Thread.sleep(300);    //休息300毫秒
        } catch (InterruptedException e) { e.printStackTrace(); } if(this.num>0){ //打印買票信息
            System.out.println(Thread.currentThread().getName() + "買票: " + this.num--); } } public static void main(String[] args) { TicketThreadR ticketThread = new TicketThreadR(); new Thread(ticketThread,"售票口一").start();    //線程一
        new Thread(ticketThread,"售票口二").start();    //線程一
        new Thread(ticketThread,"售票口三").start();    //線程一
 } }

 

多個線程共享同一資源的時候需要進行同步,但是過多的同步會造成死鎖.

 

什么叫死鎖?死鎖產生的主要原因是什么?死鎖產生的必要條件,如何解決死鎖?​

死鎖指在多道程序系統中,一組進程中的每一個進程均無限期的等待該被改組進程中的另一個進程所以占有且永遠不會釋放的資源,這種現象稱為系統處於死鎖狀態.​

死鎖產生的原因主要有2:​

  1.競爭資源,系統提供的資源數量有限,不能滿足每個進程的要求

  2.多道程序運行時,.進程推進順序不合理

產生死鎖的必要條件

  1.互斥使用資源

  2.占用並等待資源

  3.不可搶奪資源

  4.循環等待資源

解決死鎖的方法

  1.預防死鎖:破壞死鎖產生的條件(除過互斥條件,因為破壞互斥條件不現實)​

  2.避免死鎖

  3.檢測與排除

  4.置之不理

 

轉載請注明出處:[http://www.cnblogs.com/dennisit/archive/2013/02/24/2925288.html]

在線交談


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM