在很多博客中用這樣一個例子來說明
Runnable更容易實現資源共享,能多個線程同時處理一個資源。
看代碼:
public static void main(String[] args) { new MyThread().start(); new MyThread().start(); } class MyThread extends Thread{ private int ticket = 5; public void run(){ while(true){ System.out.println("Thread ticket = " + ticket--); if(ticket < 0){ break; } } } }
輸出結果:
Thread ticket = 5 Thread ticket = 5 Thread ticket = 4 Thread ticket = 4 Thread ticket = 3 Thread ticket = 2 Thread ticket = 3 Thread ticket = 1 Thread ticket = 2 Thread ticket = 0 Thread ticket = 1 Thread ticket = 0
這樣多賣了一半的票顯然不合理,用runnable試試:
public static void main(String[] args) { // TODO Auto-generated method stub MyThread2 m=new MyThread2(); new Thread(m).start(); new Thread(m).start(); } class MyThread2 implements Runnable{ private int ticket = 5; public void run(){ while(true){ System.out.println("Runnable ticket = " + ticket--); if(ticket < 0){ break; } } } }
輸出結果:
Runnable ticket = 5 Runnable ticket = 4 Runnable ticket = 3 Runnable ticket = 2 Runnable ticket = 1 Runnable ticket = 0
這樣的結果才合理。
很明顯這個例子完全錯誤,多賣票的原因根本不是因為Runnable和Thread的區別,看調用就知道了。
使用Thread的時候是這樣調用的:
new MyThread().start(); new MyThread().start();
而使用Runnable的時候是這樣調用的:
new Thread(m).start(); new Thread(m).start();
新建了兩個MyThread對象去賣票,不賣兩倍票才怪呢,而Runnable賣票的時候是同一個Runnable對象,肯定只賣一倍票,所以這個例子根本沒體現Runnable和Thread的區別,再來看一個例子:
public class TicketThread extends Thread{ private int ticket = 10; public void run(){ for(int i =0;i<10;i++){ synchronized (this){ if(this.ticket>0){ try { Thread.sleep(100); System.out.println(Thread.currentThread().getName()+"賣票---->"+(this.ticket--)); } catch (InterruptedException e) { e.printStackTrace(); } } } } } public static void main(String[] arg){ TicketThread t1 = new TicketThread(); new Thread(t1,"線程1").start(); new Thread(t1,"線程2").start(); } }
輸出結果:
線程1賣票—->10 線程1賣票—->9 線程1賣票—->8 線程2賣票—->7 線程2賣票—->6 線程1賣票—->5 線程1賣票—->4 線程2賣票—->3 線程2賣票—->2 線程1賣票—->1
(這里必須使用synchronized,否則會出現重復賣某一張票的情況,當然這點和本篇文章無關,這里不做詳述。)
這樣就達到了賣一倍票的結果,沒毛病。這樣看起來,Thread和Runnable豈不是沒區別了?
找到答案很簡單,點進去看Thread源碼就知道了
public class Thread implements Runnable {}
可以看出,Thread實現了Runnable接口,這和上面例子中的MyThread2一樣了,當我們使用
TicketThread t1 = new TicketThread(); new Thread(t1,"線程1").start();
這種寫法時,會被認為智障。。。。沒錯,就是智障,這是脫褲子放屁的寫法,標准寫法應該是:
TicketThread t1 = new TicketThread(); t1.start();
看看源碼:
public Thread(Runnable target) { init(null, target, "Thread-" + nextThreadNum(), 0); }
new Thread( new TicketThread(),"線程1").start();這種寫法里面,TicketThread會被當做一個Runnable,那我還要寫一個TicketThread類干嘛?所以這就是脫褲子放屁。
說了這么多,那兩者的區別到底是什么?
來看看Runnable的代碼:
說了這么多,那兩者的區別到底是什么?
來看看Runnable的代碼:
public interface Runnable { /** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread causes the object's * <code>run</code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code>run</code> is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */ public abstract void run(); }
結論就是:
1、效果上沒區別,寫法上的區別而已。2、沒有可比性,Thread實現了Runnable接口並進行了擴展,我們通常拿來進行比較只是寫法上的比較,而Thread和Runnable的實質是實現的關系,不是同類東西。
無論你使用Runnable還是Thread,都有一個new Thread的過程,效果上最后都是new Thread,然后執行run方法。寫法上的區別無非就是你是new Thead還是new你自定義的thread,如果你有復雜的線程操作需求,那就自定義Thread,如果只是簡單的在子線程run一下任務,那就自己實現runnable,當然如果自己實現runnable的話可以多一個繼承(自定義Thread必須繼承Thread類,java單繼承規定導致不能在繼承別的了)。
鏈接:https://www.jianshu.com/p/9c9a11092f26
