咱們知道Java傳統多線程的實現有兩種方法,繼承Thread類或者實現Runnable便可.線程啓動時調用start()方法.html
實現Runnable接口相比繼承Thread類有以下好處:java
1.避免單繼承的侷限,一個類能夠同時實現多個接口多線程
2.適合資源的共享.ide
類繼承runnable接口,實現(覆蓋)run方法,實例化線程調用start執行run方法。this
實現多線程模擬售票點賣票來講明實現Runnable便可能夠達到資源共享的目的.atom
使用繼承Thread類的多線程售票實現spa
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張票,線程之間沒有進行資源共享code
實現Runnable的售票線程htm
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對象之間的數據共享:
一、將共享數據封裝在另一個對象中,而後將這個對象逐一傳遞給各個Runnable對象。每一個線程對共享數據的操做方法也分配到那個對象身上去完成,這樣容易實現針對該數據進行的各個操做的互斥和通訊。
二、將這些Runnable對象做爲某一個類中的內部類,共享數據做爲這個外部類中的成員變量,每一個線程對共享數據的操做方法也分配給外部類,以便實現對共享數據進行的各個操做的互斥和通訊,做爲內部類的各個Runnable對象調用外部類的這些方法。
三、上面兩種方式的組合:將共享數據封裝在另一個對象中,每一個線程對共享數據的操做方法也分配到那個對象身上去完成,對象做爲這個外部類中的成員變量或方法中的局部變量,每一個線程的Runnable對象做爲外部類中的成員內部類或局部內部類。
四、總之,要同步互斥的幾段代碼最好是分別放在幾個獨立的方法中,這些方法再放在同一個類中,這樣比較容易實現它們之間的同步互斥和通訊。
(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.置之不理