在Java中要想實現多線程操作有兩種方法:
(1) 繼承Thread類
(2) 實現Runnable接口
一. 繼承Thread類
Thread類是在java.lang包中定義的,一個類只要繼承了Thread類,此類就稱爲多線程實現類。在Thread子類中,必須明確的覆寫Thread類中的run方法,此方法爲線程的主體。下面進行多線程的實現
【繼承Thread類實現多線程】
class MyThread extends Thread {
private String name;
public MyThread(String name) {
this.name = name;
}
@Override
public void run() { // 覆寫Thread類中的run()方法
for (int i = 0; i < 10; i++) {
System.out.println(name + "運行,i=" + i);
}
}
};
public class TreadDemo01 {
public static void main(String[] args) {
MyThread mt1 = new MyThread("線程A");
MyThread mt2 = new MyThread("線程B");
mt1.start();
mt2.start();
}
}
運行的結果:
線程A運行,i=0
線程B運行,i=0
線程B運行,i=1
線程B運行,i=2
線程B運行,i=3
線程B運行,i=4
線程B運行,i=5
線程B運行,i=6
線程A運行,i=1
線程B運行,i=7
線程A運行,i=2
線程B運行,i=8
線程A運行,i=3
線程A運行,i=4
線程B運行,i=9
線程A運行,i=5
線程A運行,i=6
線程A運行,i=7
線程A運行,i=8
線程A運行,i=9
編譯運行的結果可以看出兩個線程對象是交錯運行的,哪個線程對象搶到CPU資源,哪個線程就可以運行,所以程序每次運行的結果是不同的,在線程啓動時調用的start()方法,但是實際上調用的是run()方法定義的主題。
注意:一個線程對象不能重複調用start()方法,否則會異常。
如果一個類只能靠繼承Thread類才能實現多線程,則必定會受到單繼承侷限的影響,所以,一般要想實現多線程,還可以通過實現Runnable接口來完成。
二. 實現Runnable接口
在Java中也可以通過實現Runnable接口的方式來實現多線程,Runnable接口中定義了一個抽象方法:
public void run()
使用Runnable接口實現多線程:
class MyThread2 implements Runnable {
private String name;
public MyThread2(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(name + "運行,i=" + i);
}
}
}
以上代碼時通過Runnable接口來實現多線程的,但是從前面的代碼中可以知道,一個線程必須通過Thread類中的start()方法開啓。如果繼承了Thread類,則可以直接調用此方法,但是在實現Runnable接口時,此接口中並沒有start()方法,那麼該如何啓動多線程?實際上,此時還是依靠Thread類完成的。
【使用Thread類來啓動線程】
class MyThread2 implements Runnable {
private String name;
public MyThread2(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(name + "運行,i=" + i);
}
}
}
public class RunnableDemo01 {
public static void main(String[] args) {
MyThread2 mt1 = new MyThread2("線程A");
MyThread2 mt2 = new MyThread2("線程B");
Thread th1 = new Thread(mt1);
Thread th2 = new Thread(mt2);
th1.start();
th2.start();
}
}
運行結果爲:
線程A運行,i=0
線程B運行,i=0
線程B運行,i=1
線程B運行,i=2
線程B運行,i=3
線程B運行,i=4
線程B運行,i=5
線程B運行,i=6
線程B運行,i=7
線程B運行,i=8
線程B運行,i=9
線程A運行,i=1
線程A運行,i=2
線程A運行,i=3
線程A運行,i=4
線程A運行,i=5
線程A運行,i=6
線程A運行,i=7
線程A運行,i=8
線程A運行,i=9
以上兩種方法可以發現,無論使用哪種方式,最終都必須依靠Thread類才能啓動多線程。
三. Thread類和Runnable接口
通過Thread類和Runnable接口都可以實現多線程,那麼兩者有哪些聯繫和區別吶?下面觀察Thread類的定義
public class Thread extends Object implements Runnable
從Thread類的定義可以發現,Thread類也是Runnable接口的子類,但在Thread類中並沒有完全地實現Runnable接口中的run()方法。
Thread類的定義:
*/
public
class Thread implements Runnable {
/* Make sure registerNatives is the first thing <clinit> does. */
private static native void registerNatives();
static {
registerNatives();
}
public void run(){
if(target!=null){
target.run;
}
}
從定義中可以發現,在Thread類中的run()方法調用的Runnable接口中的run()方法,也就是說此方法是由Runnable子類完成的,所以如果要通過Thread類實現多線程,則必須覆寫run()方法。
實際上Thread類和Runnable接口也是有區別的,如果一個類繼承Thread類,則不適合於多個線程共享資源,而實現Runnable接口,就可以直接實現資源的共享。
【繼承thread類而不能共享資源】
class MyThread extends Thread{ // 繼承Thread類,作爲線程的實現類
private int ticket = 5 ; // 表示一共有5張票
public void run(){ // 覆寫run()方法,作爲線程 的操作主體
for(int i=0;i<100;i++){
if(this.ticket>0){
System.out.println("賣票:ticket = " + ticket--) ;
}
}
}
};
public class ThreadDemo04{
public static void main(String args[]){
MyThread mt1 = new MyThread() ; // 實例化對象
MyThread mt2 = new MyThread() ; // 實例化對象
mt1.start() ; // 調用線程主體
mt2.start() ; // 調用線程主體
}
};
運行結果:
賣票:ticket=5
賣票:ticket=5
賣票:ticket=4
賣票:ticket=3
賣票:ticket=2
賣票:ticket=1
賣票:ticket=4
賣票:ticket=3
賣票:ticket=2
賣票:ticket=1
以上程序通過Thread類實現多線程,程序中實現了2個線程,但是兩個線程分別買了各自的5張票,並沒有達到資源共享的目的。
【實現Runnable接口可以資源共享】
class MyThread2 implements Runnable {
private int ticket = 5;
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (ticket > 0) {
System.out.println("賣票:ticket=" + ticket--);
}
}
}
}
public class RunnableDemo01 {
public static void main(String[] args) {
MyThread2 mt1 = new MyThread2();
new Thread(mt1).start();
new Thread(mt1).start();
new Thread(mt1).start();
}
}
運行結果:
賣票:t這裏寫代碼片
icket=5
賣票:ticket=4
賣票:ticket=2
賣票:ticket=3
賣票:ticket=1
從程序的運行結果可以發現,雖然啓動了線程,但是3個線程一共才賣5張票,即ticket屬性被所有屬性被所有的行程對象共享。
可見,實現Runable接口相對於繼承Thread類說,有如下顯著的優勢:
(1) 適合多個相同程序代碼的線程去處理同一個資源的情況
(2) 可以避免由於Java單繼承特性帶來的侷限
(3) 增強了程序的健壯性,代碼能夠被多個線程共享,代碼與數據都是相互獨立的
所以,在開發中建議使用Runnable接口實現多線程。