直接上代碼看的更清晰:html
package org.thread.demo; public class MyThread extends Thread{ private String name; public MyThread(String name) { super(); this.name = name; } public void run(){ for(int i=0;i<10;i++){ System.out.println("線程開始:"+this.name+",i="+i); } } } package org.thread.demo; public class ThreadDemo01 { public static void main(String[] args) { MyThread mt1=new MyThread("線程a"); MyThread mt2=new MyThread("線程b"); mt1.run(); mt2.run(); } }
運行發現結果頗有規律,先第一個對象執行,而後第二個對象執行,並無相互運行。在JDK的文檔中能夠發現,一旦調用start方法,則會經過JVM找到run方法。下面啓動start方法啓動線程:java
package org.thread.demo; public class ThreadDemo01 { public static void main(String[] args) { MyThread mt1=new MyThread("線程a"); MyThread mt2=new MyThread("線程b"); mt1.start(); mt2.start(); } }
這樣程序能夠正常完成交互式運行。那麼爲啥非要使用start方法啓動多線程呢?多線程
在JDK的安裝路徑下,src.zip是所有的java源程序,經過此代碼找到Thread中的start方法的定義,能夠發現此方法中使用了private native void start0();其中native關鍵字表示能夠調用操做系統的底層函數,那麼這樣的技術稱爲JNI技術(java Native Interface)ide
Runnable接口函數
在實際開發中一個多線程的操做不多使用Thread類,而是經過Runnable接口完成。this
package org.runnable.demo; public class MyThread implements Runnable{ private String name; public MyThread(String name) { this.name = name; } public void run(){ for(int i=0;i<100;i++){ System.out.println("線程開始:"+this.name+",i="+i); } } }
可是在使用Runnable定義的子類中沒有start方法,只有Thread類中才有。此時觀察Thread類,有一個構造方法:public Thread(Runnable targer) 此構造方法接受Runnable的子類實例,也就是說能夠經過Thread類來啓動Runnable實現的多線程。(start方法能夠協調系統的資源):spa
package org.runnable.demo; import org.runnable.demo.MyThread; public class ThreadDemo01 { public static void main(String[] args) { MyThread mt1=new MyThread("線程a"); MyThread mt2=new MyThread("線程b"); new Thread(mt1).start(); new Thread(mt2).start(); } }
兩種實現方式的區別和聯繫:操作系統
在程序開發中只要是多線程確定永遠以實現Runnable接口爲主,由於實現Runnable接口相比繼承Thread類有以下好處:線程
避免點繼承的侷限,一個類能夠實現多個接口。code
適合於資源的共享
以賣電影券程序爲例,經過Thread類完成:
package org.demo.dff; public class MyThread extends Thread{ private int ticket=10; public void run(){ for(int i=0;i<20;i++){ if(this.ticket>0){ System.out.println("賣電影券:ticket"+this.ticket--); } } } }
下面經過三個線程對象,同時賣電影券:
package org.demo.dff; public class ThreadTicket { public static void main(String[] args) { MyThread mt1=new MyThread(); MyThread mt2=new MyThread(); MyThread mt3=new MyThread(); mt1.start();//每一個線程都各賣了10張,共賣了30張電影券 mt2.start();//但實際只有10張電影券,每一個線程都賣本身的電影券 mt3.start();//沒有達到資源共享 } }
若是用Runnable就能夠實現資源共享,下面看例子:
package org.demo.runnable; public class MyThread implements Runnable{ private int ticket=10; public void run(){ for(int i=0;i<20;i++){ if(this.ticket>0){ System.out.println("賣電影券:ticket"+this.ticket--); } } } } package org.demo.runnable; public class RunnableTicket { public static void main(String[] args) { MyThread mt=new MyThread(); new Thread(mt).start();//同一個mt,可是在Thread中就不能夠,若是用同一個實例化對象mt,就會出現異常 new Thread(mt).start();new Thread(mt).start(); } }
雖然如今程序中有三個線程,可是一共賣了10張電影券,也就是說使用Runnable實現多線程能夠達到資源共享目的。
Runnable接口和Thread之間的聯繫:
public class Thread extends Object implements Runnable
發現Thread類也是Runnable接口的子類。
Thread類中run()和start()方法的區別以下:
run()方法: 在本線程內調用該Runnable對象的run()方法,能夠重複屢次調用;
start()方法: 啓動一個線程,調用該Runnable對象的run()方法,不能屢次啓動一個線程;
package com.ljq.test; public class ThreadTest { /** * 觀察直接調用run()和用start()啓動一個線程的差異 * * @param args * @throws Exception */ public static void main(String[] args){ Thread thread=new ThreadDemo(); //第一種 //代表: run()和其餘方法的調用沒任何不一樣,main方法按順序執行了它,並打印出最後一句 //thread.run(); //第二種 //代表: start()方法從新建立了一個線程,在main方法執行結束後,因爲start()方法建立的線程沒有運行結束, //所以主線程未能退出,直到線程thread也執行完畢.這裏要注意,默認建立的線程是用戶線程(非守護線程) //thread.start(); //第三種 //一、爲何沒有打印出100句呢?由於咱們將thread線程設置爲了daemon(守護)線程,程序中只有守護線程存在的時候,是能夠退出的,因此只打印了七句便退出了 //二、當java虛擬機中有守護線程在運行的時候,java虛擬機會關閉。當全部常規線程運行完畢之後, //守護線程無論運行到哪裏,虛擬機都會退出運行。因此你的守護線程最好不要寫一些會影響程序的業務邏輯。不然沒法預料程序到底會出現什麼問題 //thread.setDaemon(true); //thread.start(); //第四種 //用戶線程能夠被System.exit(0)強制kill掉,因此也只打印出七句 thread.start(); System.out.println("main thread is over"); System.exit(1); } public static class ThreadDemo extends Thread{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("This is a Thread test"+i); } } } }
放大招(哈哈),總結:
1) start:
用start方法來啓動線程,真正實現了多線程運行,這時無需等待run方法體代碼執行完畢而直接繼續執行下面的代碼。經過調用Thread類的start()方法來啓動一個線程,這時此線程處於就緒(可運行)狀態,並無運行,一旦獲得cpu時間片,就開始執行run()方法,這裏方法run()稱爲線程體,它包含了要執行的這個線程的內容,Run方法運行結束,此線程隨即終止。
2) run:
run()方法只是類的一個普通方法而已,若是直接調用Run方法,程序中依然只有主線程這一個線程,其程序執行路徑仍是隻有一條,仍是要順序執行,仍是要等待run方法體執行完畢後纔可繼續執行下面的代碼,這樣就沒有達到多線程的目的。
總結:調用start方法方可啓動線程,而run方法只是thread的一個普通方法調用,仍是在主線程裏執行