如果一個類繼承Thread,則不適合資源共享。但是如果實現了Runable接口的話,則很容易的實現資源共享。實現Runnable接口或callable接口,適合多個相同或不同的程序代碼的線程去共享同一個資源。
多個線程共享數據分兩種情況:
1、如果多個線程執行同一個Runnable實現類中的代碼,此時共享的數據放在Runnable實現類中;
2、如果多個線程執行不同的Runnable實現類中的代碼,此時共享數據和操作共享數據的方法封裝到一個對象中,在不同的Runnable實現類中調用操作共享數據的方法。
一、 相同程序代碼的多個線程共享一個資源
如果有多個線程在同時運行同一段段代碼,可以使用同一個Runnable
實現類,多個線程可以共享一個實現類對象,共享數據作為這個Runnable實現類
的全局變量
賣票案例:
public class Demo08 { public static void main(String[] args) { //創建線程任務對象 Ticket ticket = new Ticket(); //創建三個窗口對象 Thread t1 = new Thread(ticket, "窗口1"); Thread t2 = new Thread(ticket, "窗口2"); Thread t3 = new Thread(ticket, "窗口3"); //賣票 t1.start(); t2.start(); t3.start(); } static class Ticket implements Runnable { //Object lock = new Object(); ReentrantLock lock = new ReentrantLock(); private int ticket = 10; public void run() { String name = Thread.currentThread().getName(); while (true) { sell(name); if (ticket <= 0) { break; } } } private synchronized void sell(String name) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }if (ticket > 0) { System.out.println(name + "賣票:" + ticket); ticket--; } } } }
ticket就是全局變量,作為三個線程的共享數據。
二、 不同程序代碼的多個線程共享一個資源
如果每個線程執行的代碼不同,將共享數據和操作共享數據的方法封裝在一個對象中,在不同的Runnable實現類調用操作共享數據的方法。
銀行轉賬案例:
創建一個對象,對象中有操作共享數據的方法
public class Bank { private volatile int count =0;//賬戶余額 //存錢 public void addMoney(int money){ count +=money; System.out.println(System.currentTimeMillis()+"存進:"+money); } //取錢 public void subMoney(int money){ if(count-money < 0){ System.out.println("余額不足"); return; } count -=money; System.out.println(+System.currentTimeMillis()+"取出:"+money); } //查詢 public void lookMoney(){ System.out.println("賬戶余額:"+count); } }
創建兩個線程,線程中分別調用共享數據所在對象中不同的方法,
public class SyncThreadTest { public static void main(String args[]){ final Bank bank=new Bank(); Thread tadd=new Thread(new Runnable() { public void run() { // TODO Auto-generated method stub while(true){ try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } bank.addMoney(100); bank.lookMoney(); System.out.println("\n"); } } }); Thread tsub = new Thread(new Runnable() { public void run() { // TODO Auto-generated method stub while(true){ bank.subMoney(100); bank.lookMoney(); System.out.println("\n"); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }); tsub.start(); tadd.start(); } }
如果一個類繼承Thread,則不適合資源共享。並不是不可以實現資源共享
生產者與消費者問題
包子鋪線程生產包子,吃貨線程消費包子。當包子沒有時(包子狀態為false),吃貨線程等待,包子鋪線程生產包子(即包子狀態為true),並通知吃貨線程(解除吃貨的等待狀態),因為已經有包子了,那么包子鋪線程進入等待狀態。接下來,吃貨線程能否進一步執行則取決於鎖的獲取情況。如果吃貨獲取到鎖,那么就執行吃包子動作,包子吃完(包子狀態為false),並通知包子鋪線程(解除包子鋪的等待狀態),吃貨線程進入等待。包子鋪線程能否進一步執行則取決於鎖的獲取情況。



實現Runnable接口比繼承Thread類所具有的優勢:
1. 適合多個相同的程序代碼的線程去共享同一個資源。
2. 可以避免java中的單繼承的局限性。
3. 增加程序的健壯性,實現解耦操作,代碼可以被多個線程共享,代碼和線程獨立。
4. 線程池只能放入實現Runable或Callable類線程,不能直接放入繼承Thread的類。