1. 程式人生 > >Runnable可以實現資源共享但Thread不能實現資源共享的原因

Runnable可以實現資源共享但Thread不能實現資源共享的原因

轉自:http://blog.csdn.net/javaniceyou/article/details/6859305

執行緒的兩種實現方式,通過實現Runnable介面的執行緒方式可以實現資源的共享,而繼承Thread則不可以,原因何在?先看下面兩段程式碼:

通過Thread實現執行緒:

Java程式碼 複製程式碼
  1. //使用Thread實現執行緒不能實現資源共享 
  1. //使用Thread實現執行緒不能實現資源共享
  2. class MyThread extends Thread  
  3. {     
  4.     privateint ticket=5;  
  5.     private String name;  
  6.     public
     MyThread(String name ){  
  7.         this.name=name;  
  8.     }  
  9.     publicvoid run(){  
  10.         for(int i=0;i<10;i++){  
  11.             if(ticket>5){  
  12.                 System.out.println("執行緒"+name+"賣票"+i);  
  13.             }  
  14.         }  
  15.     }  
  16. }  
  17. publicclass ThreadDemo02  
  18. {  
  19.     publicstaticvoid main(String args[]){  
  20.         MyThread A = new MyThread("A");    
  21.         MyThread B = new MyThread("B");  
  22.         A.start();  
  23.         B.start();  
  24.     }  
  25. }  

通過Runnable實現:

Java程式碼 複製程式碼
  1.  
    1. //使用Runnable實現執行緒可以實現資源共享
    2. class MyThread implements Runnable  
    3. {  
    4.     privateint ticket=5;  
    5.     private String name;  
    6.     public
       MyThread(String name){  
    7.         this.name=name;  
    8.     }  
    9.     publicvoid run(){  
    10.         for(int i=1;i<=10;i++){  
    11.             if(ticket>0){  
    12.                 System.out.println("執行緒"+name+"賣票"+(ticket--));  
    13.                 }  
    14.         }  
    15.     }  
    16. }  
    17. publicclass RunnableDemo02  
    18. {  
    19.     publicstaticvoid main(String args[]){  
    20.         MyThread A = new MyThread("A");  //例項化執行緒要執行的任務
    21.         Thread Ta = new Thread(A);    //例項兩個執行緒物件,實際傳遞的是一個任務
    22.         Thread Tb = new Thread(A);    //因為兩個執行緒執行的是一個任務,所以資源是共享的
    23.         Ta.start();  
    24.         Tb.start();  
    25.     }  
    26. }  

解釋:

因為一個執行緒只能啟動一次,通過Thread實現執行緒時,執行緒和執行緒所要執行的任務是捆綁在一起的。也就使得一個任務只能啟動一個執行緒,不同的執行緒執行的任務是不相同的,所以沒有必要,也不能讓兩個執行緒共享彼此任務中的資源。

一個任務可以啟動多個執行緒,通過Runnable方式實現的執行緒,實際是開闢一個執行緒,將任務傳遞進去,由此執行緒執行。可以例項化多個 Thread物件,將同一任務傳遞進去,也就是一個任務可以啟動多個執行緒來執行它。這些執行緒執行的是同一個任務,所以他們的資源是共享。

兩種不同的執行緒實現方式本身就決定了其是否能進行資源共享。

個人理解:網上很多文章講述可以使用Runnable實現資源共享,當然有的使用繼承Thread也可以,但我還是表示懷疑這種做法是否真的可以實現資源共享,在共享資源的過程中並沒有使用同步欄位,這樣是否會引起資源衝突,還需進一步探討,如有牛人解釋,不勝感激!

找到以下解釋讓我感到我的想法是正確的。

我麼知道Java傳統多執行緒的實現有兩種方法,繼承Thread類或者實現Runnable即可.執行緒啟動時呼叫start()方法.

實現Runnable介面相比繼承Thread類有如下好處:

1.避免單繼承的侷限,一個類可以同時實現多個介面

2.適合資源的共享.

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

使用繼承Thread類的多執行緒售票實現

複製程式碼
package org.dennist.thread.demo;/** * *  TicketThread.java     * *  @version : 1.1 *   *  @author  : 蘇若年    <a href="mailto:[email protected]">傳送郵件</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:[email protected]">傳送郵件</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:[email protected]">傳送郵件</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();    }}
複製程式碼

如果想解決這樣的問題(這個問題我是沒有太看明白為何會輸出這樣的結果,兩個4是什麼情況下輸出的,雖然有問題,但是能確定的是如果要實現資源共享,必須的是有同步操作才可以,不然會出現執行緒安全問題,同步方法可以避免資源競爭),就必須使用同步,所謂的同步就是指多個操作在同一個時間段內只有一個執行緒進行,其他執行緒要等待此執行緒完成之後才可以繼續執行.

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

JAVA多執行緒同步主要依賴於若干方法和關鍵字
  1  wait方法

  2  notify方法和notifyAll方法

  3  synchronized關鍵字

  4 atomic action(原子操作)

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

  1.同步程式碼塊

  2.同步方法

同步程式碼塊

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

  synchronized(物件){   //一般都是講this鎖定

         //鎖定物件

     }

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

複製程式碼
package org.dennist.thread.demo;/** * *  TicketThreadR.java     * *  @version : 1.1 *   *  @author  : 蘇若年    <a href="mailto:[email protected]">傳送郵件</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:[email protected]">傳送郵件</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.置之不理​


相關推薦

Runnable可以實現資源共享Thread不能實現資源共享原因

轉自:http://blog.csdn.net/javaniceyou/article/details/6859305 執行緒的兩種實現方式,通過實現Runnable介面的執行緒方式可以實現資源的共享,而繼承Thread則不可以,原因何在?先看下面兩段程式碼: 通過T

實現Runnable,輕鬆實現多執行緒間的資源共享

public class Test { class TestRunnable implements Runnable { private String TName="TName"; private Object obj=new O

Samba服務搭建實現Windows與Linux系統之間資源共享

img -o common system 用戶 emctl 服務 ssd ons 1安裝服務#yum -y install samba samba-client samba-common查看#rpm –qa | grep samba修改配置文件先備份#cp smb.con

在Ubuntu16.04下搭建samba,實現linux與windows之間的資源共享

轉載於http://www.linuxdiyf.com/linux/24260.html   1、開始需要我們做的是先在我們的ubuntu下安裝好samba: 安裝samba:sudo apt-get install samba 安裝smbclient:sudo

【Java並發編程】之六:RunnableThread實現多線程的區別(含代碼)

技術分享 runnable 避免 實際應用 details div 一個 預測 enter 轉載請註明出處:http://blog.csdn.net/ns_code/article/details/17161237 Java中實現多線程有兩種方法:繼承Thre

Java併發程式設計(6):RunnableThread實現多執行緒的區別(含程式碼)

Java中實現多執行緒有兩種方法:繼承Thread類、實現Runnable介面,在程式開發中只要是多執行緒,肯定永遠以實現Runnable介面為主,因為實現Runnable介面相比繼承Thread類有如下優勢: 1、可以避免由於Java的單繼承特性而帶來的侷限; 2、增強程式的健壯性,程式碼能夠被多個執行

Java並發編程(6):RunnableThread實現多線程的區別(含代碼)

線程休眠 ket out dde 可能 休眠 stat for oid Java中實現多線程有兩種方法:繼承Thread類、實現Runnable接口,在程序開發中只要是多線程,肯定永遠以實現Runnable接口為主,因為實現Runnable接口相比繼承Thread類有如下優

Java 實現多執行緒Thread Runnable Callable 三種方式

Java 多執行緒 java 實現 多執行緒 三種方法 1. 繼承Thread重寫 run方法。 2.實現Runnable的run方法。無返回值。一個類可以實現多個介面。 3.實現Callable的call方法。有返回值,可以丟擲執行緒錯誤。一個類可以實現多個介面。 public class

靜態資源的虛擬路徑和獨立靜態資源管理系統的實現

一、什麼是虛擬路徑?         舉個例子:上傳一張圖片放到:D://group/29015054169244_投影.png        &nbs

Nginx + Lets'encrypt 實現HTTPS訪問七牛空間資源

上一篇文章 為七牛雲端儲存空間繫結自定義域名,並使用七牛雲提供的免費SSL證書,將自定義加名升級為HTTPS 我們提到利用七牛的免費SSL證書,將自定義加名升級為HTTPS的方法。 不知道有沒有小夥伴會像我一樣擔心一年七牛的SSL證書不免費了怎麼辦?每個域名每年都要幾千塊的支出對於個人和小企業來說還是一筆不

一行命令實現多渠道打包並自動進行資源混淆(Walle,AndResGuard)

多渠道打包和資源混淆的作用這裡我就不再闡述了。網上有很多。 本篇部落格我們來介紹一下如何實現多渠道打包之前先進行資源混淆,這樣一來我們打出來的包都是已經進行過資源混淆的了。 如果你還有其他的需求,也可以按照這個方法實現。 多渠道打包(Walle) 這裡多渠道打包

資源任務排程演算法實現(大資料雲端計算作業來的)

實驗目的 本實驗將引導學生對雲端計算任務排程演算法的相關研究現狀進行深入分析和研究,從影響使用者任務的執行效率和系統資源的使用效率的角度出發,在現有的雲端計算任務排程演算法的基礎上,進行理論創新,從模型高效和演算法高效2個層面上設計雲端計算任務排程模型、演算法並實現。 實驗思路 實驗主要

【Java併發程式設計】之六:RunnableThread實現多執行緒的區別(含程式碼)

    Java中實現多執行緒有兩種方法:繼承Thread類、實現Runnable介面,在程式開發中只要是多執行緒,肯定永遠以實現Runnable介面為主,因為實現Runnable介面相比繼承Th

多執行緒兩種實現方式的區別Thread Runnable

多執行緒兩種實現方式的區別多執行緒的兩種實現方式 Thread Runnable 兩者的區別首先:使用Runnable介面與Thread類相比較,解決了單根繼承的定義侷限性,所以不管後面的區別和聯絡是什麼,至少這一點上就已經下了死定義– 如果要使用一定是用Runnable介面

用python檔案實現自動下載連結中的資源

import os from subprocess import call print("") print("Downloading...") if not os.path.exists("UCI HAR Dataset.zip"):     call(

java 實現佔用特定百分比的CPU資源

publicclass cpu{publicstaticvoid main(String[] args){long startTime =0;// 開始時間int busyTime =Integer.parseInt(args[0]);// 繁忙時間int idleTime

java 實現百度熊掌號歷史資源記錄提交

最近在做一個需求,需要將大量的歷史記錄url提交給百度熊掌號資源搜尋平臺,雖然熊賬號給提供了手動提交的工具,但是這種方式的提交費時費力,尤其是在有很多的url需要提交時使用這個方式提交很明顯效率低下,所以可以採用提供api提交的方式,一   百度熊掌號賬號獲取(這個可以自己百

eclipse外掛開發入門——使用command實現資源管理器中定位資源

Eclipse外掛開發之command Eclipse提供了三種命令與操作的方式:動作ActionSets、彈出選單popupmenus、命令Command,其中前兩種因為耦合過於緊密,在未來版本中可能棄用,不建議使用。 ActionSets方式使用的是擴充套件點org.

windows資源管理器的實現例項

Windows的資源管理器想必大家都用過,該程式的視窗一分為二,左邊的視窗顯示本機當前所有驅動器以及驅動器中的所有資料夾,當用戶單擊資料夾後,如果該資料夾下面還有子資料夾,則上層資料夾展開顯示下級的資料夾;否則,右邊的視窗顯示選擇資料夾下的檔案。那麼這個程式是如

Java併發03:多執行緒實現三方式:繼承Thread類、實現Runnable介面、實現Callable介面

本章主要對Java多執行緒實現的三種方式進行學習。 1.序言 在JDK5版本之前,提供了兩種多執行緒的實現方式: 繼承Thread類,重寫run()方法 實現Runnable介面,實現run()方法 這兩種種方式的本質都是一個:實現Runna