因爲售票的例子博友們已經列舉太多了,主要取決於是否採用new Thread(Runnable target, String name)構造創建實例,如果不想看例子的,直接看結果分析吧
1.Thread和Runuable的關係
public interface Runnable {
public abstract void run();
}
public class Thread implements Runnable {
…
}
從這兩段代碼我們可以知道Runnable是一個接口,而Thread實現了這個接口,即Thread是Runnable的實現類。
2.Thread和Runnable的比較
當要聯網或處理耗時邏輯的時候,我們要開啓子線程,那選用那種方式好呢?Runnable具有以下優勢:
(1)java不支持多繼承,實現Runnable還可以繼承其它類,調用其功能,更具靈活性
(2)增加程序的健壯性,代碼可以被多個線程共享,代碼和數據獨立
(3)適合多個相同的程序代碼的線程去處理同一個資源(有很多博友有這樣的說法,個人存疑,因爲Thread方式也可以達到同樣目的)
還是用售票的例子吧,話不多說,上代碼:
/**
* Time: xxx
* Author: xxx
* Description: 繼承Thread方式實現多線程併發售票
*/
public class TicketThread extends Thread {
//4張票
private int ticket = 4;
@Override
public void run() {
for (int i = 0; i < 4; i++) {
synchronized (this) {
if (this.ticket>0) {
try{
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+"賣票--->"+(ticket--));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
public static void main(String[] args) {
TicketThread ticketThread = new TicketThread();
new Thread(ticketThread, "線程1").start();
new Thread(ticketThread, "線程2").start();
new Thread(ticketThread, "線程3").start();
}
}
結果如下:
線程2賣票—>3
線程3賣票—>1
線程1賣票—>2
/**
* Time: xxx
* Author: xxx
* Description: 實現Runnable的方式實現多線程賣票
*/
public class TicketRunnable implements Runnable {
private int ticket = 3;
@Override
public void run() {
for (int i = 0; i < 3; i++) {
synchronized (this) {
if (this.ticket>0) {
try{
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+"賣票--->"+(ticket--));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
public static void main(String[] args) {
TicketRunnable ticketRunnable = new TicketRunnable();
new Thread(ticketRunnable, "線程1").start();
new Thread(ticketRunnable, "線程2").start();
new Thread(ticketRunnable, "線程3").start();
}
}
線程2賣票—>3
線程3賣票—>1
線程1賣票—>2
有個小細節要注意一下,一定要加上synchronized同步鎖,否則可能會出現其中一個線程出現賣票—>0的情況,這很好理解,如果沒加synchronized,也就是線程不安全,當其中一個線程賣最後一張票ticket=1執行ticket–的時候此時ticket已經爲0了,但是另一個線程已經執行完了if(this.ticket>0),再執行ticket–的時候就會出現賣票—>0的情況的情況。這裏就不演示了,有興趣的博友可以自己試一下,如果一次沒出現,就運行幾次,總會中獎的。
至於博友們採用以下的寫法:
public class TicketThread extends Thread {
//3張票
private int ticket = 3;
@Override
public void run() {
for (int i = 0; i < 3; i++) {
if (this.ticket>0) {
try{
Thread.sleep(100);
System.out.println("ticket= "+ticket--);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
TicketThread ticketThread = new TicketThread();
new TicketThread().start();
new TicketThread().start();
new TicketThread().start();
}
}
結果:
ticket= 3
ticket= 3
ticket= 3
ticket= 2
ticket= 2
ticket= 2
ticket= 1
ticket= 1
ticket= 1
3. 結果分析
並不是說Runnable支持資源的共享,而Thread不支持。
本質是:構造方法傳參的問題,是要傳參,持有Runnable的引用;還是不傳參,new新的對象
以這種方式使用Thread(不傳參)時,每一個New都會產生一個擁有自己的ticket的對象,即每個對象都有各自的ticket,各操作各的,三個ticket數值變化互不影響。就相當於new了三個對象,把方法調用了三遍。如果傳參,同樣可以達到“資源共享”的效果。在使用Runnable的整個過程中(傳參了),只產生一個能操作ticket的對象,實現多個線程共同處理同一資源(ticket),即實現資源的共享;