Thread 和runnable的區別

菜雞03號發表於2016-10-26

在java中可有兩種方式實現多執行緒,一種是繼承Thread類,一種是實現Runnable介面;Thread類是在java.lang包中定義的。一個類只要繼承了Thread類同時覆寫了本類中的run()方法就可以實現多執行緒操作了,但是一個類只能繼承一個父類,這是此方法的侷限。

下面看例子:

package org.thread.demo;  
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()方法啟動執行緒:

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)

Runnable介面

在實際開發中一個多執行緒的操作很少使用Thread類,而是通過Runnable介面完成。

public interface Runnable{  
public void run();  
} 

例子:

package org.runnable.demo;  
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()可以協調系統的資源):

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類有如下好處:

  • 避免點繼承的侷限,一個類可以繼承多個介面。
  • 適合於資源的共享

以賣票程式為例,通過Thread類完成:

package org.thread;

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--+"======"+Thread.currentThread().getName());
			}
		}
	}
}


下面通過兩個執行緒物件,同時賣票:

public class MyThreadTest{
	public static void main(String []args){
		MyThread thread01=new MyThread();
		MyThread thread02=new MyThread();
		thread01.start();//每個執行緒賣了10張票,共賣了20張票
		thread02.start();//但實際只有10張票,每個執行緒都賣自己的票  //沒有達到資源共享
		
	}
}

執行結果

買票:ticket=10======Thread-0
買票:ticket=10======Thread-1
買票:ticket=9======Thread-1
買票:ticket=8======Thread-1
買票:ticket=7======Thread-1
買票:ticket=6======Thread-1
買票:ticket=9======Thread-0
買票:ticket=5======Thread-1
買票:ticket=4======Thread-1....


如果用Runnable就可以實現資源共享,下面看例子:

package org.thread;

public class MyThread02 implements Runnable{

	private int ticket=10;
	public void run(){
	for(int i=0;i<20;i++){
		if(ticket>0){
			System.out.println("賣票:ticket ="+ticket--+"===="+Thread.currentThread().getName());
		}
	}
	}
}


package org.thread;

public class MyThreadTest02 {
	public static void main(String []args){
		MyThread02 thread02=new MyThread02();
		Thread thread01=new Thread(thread02);
		Thread thread02s=new Thread(thread02);
		thread01.start();//兩個執行緒一共賣10張票
		thread02s.start();
	}
}

執行結果

賣票:ticket =10====Thread-0
賣票:ticket =9====Thread-1
賣票:ticket =8====Thread-0
賣票:ticket =7====Thread-1
賣票:ticket =6====Thread-0
賣票:ticket =5====Thread-1
賣票:ticket =4====Thread-0
賣票:ticket =3====Thread-1
賣票:ticket =2====Thread-0
賣票:ticket =1====Thread-1


雖然現在程式中有2個執行緒,但是一共賣了10張票,也就是說使用Runnable實現多執行緒可以達到資源共享目的。

相關文章