常用的實現多線程的2種方式:繼承 Thread 或者實現 Runanable 接口
Thread 類 實現了 Runnable 接口
public class Thread implements Runnable {}
//繼承 Thread 類
public class MyThread extends Thread {
/*
static Thread currentThread(),返回正在執行的線程對象的引用
public static void sleep(long millis) 當前正在執行的線程以指定毫秒數暫定(暫時停止執行),毫秒結束後繼續執行
*/
@Override
public void run() {
for (int i = 0; i <5 ; i++) {
System.out.println(this.getName()+ "賣票"+i);
// 使用Thread類中的方法 String getName(),可以返回線程的名稱。
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class MainThread {
public static void main(String[] args){
// 啓動3個線程t1,t2,t3;每個線程各循環5次!
MyThread t1=new MyThread();
MyThread t2=new MyThread();
MyThread t3=new MyThread();
t1.start();
t2.start();
t3.start();
}
}
結果:
Thread-1賣票0
Thread-0賣票0
Thread-2賣票0
Thread-0賣票1
Thread-0賣票2
Thread-0賣票3
Thread-0賣票4
Thread-1賣票1
Thread-1賣票2
Thread-2賣票1
Thread-1賣票3
Thread-2賣票2
Thread-1賣票4
Thread-2賣票3
Thread-2賣票4
實現 Runnable 接口
Runnable接口中有一個抽象方法run
public interface Runnable { public abstract void run(); }
public class DemoRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i <5 ; i++) {
System.out.println(Thread.currentThread().getName()+i);
}
}
}
public class MIanRunnable {
public static void main(String[] args){
//實例化DemoRunnable 對象
DemoRunnable r=new DemoRunnable();
//創建線程對象
Thread t0=new Thread(r,"0號窗口");
Thread t1=new Thread(r,"1號窗口");
Thread t2=new Thread(r,"2號窗口");
t0.start();
t1.start();
t2.start();
}
}
結果:
1號窗口0
1號窗口1
2號窗口0
0號窗口0
0號窗口1
0號窗口2
0號窗口3
0號窗口4
2號窗口1
2號窗口2
2號窗口3
2號窗口4
1號窗口2
1號窗口3
1號窗口4
Thread 中的 start() 方法分析
是不是用一種疑問,邏輯業務在 run 方法中,爲什麼開啓線程 要用start() 方法呢?
start() 源碼如下所示:
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
- 檢查線程的狀態,是否可以啓動;
- 把線程加入到線程group中;
- 調用了start0()方法。
Start方法中最終調用的是start0方法,並不是run方法,我們查看 start0() 方法,start0是一個native方法,也稱爲JNI(Java Native Interface)方法。JNI方法是java和其它語言交互的方式。同樣也是java代碼和虛擬機交互的方式,虛擬機就是由C++和彙編所編寫。(一句話概括 ,native 修飾的方法 實在JVM 中的實現的,也就是說 start0() 方法 jvm 中,JVM 運行的 run() ) 👉 線程的生命週期
*the Java Virtual Machine calls the run method of this thread。
java虛擬機調用這個線程的run方法。*
private native void start0();
我們看一下run 方法,發現有一個target ,target 又是什麼呢?
@Override
public void run() {
if (target != null) {
target.run();
}
}
查看 Thread 的構造函數,返現target 是一個Runnable的實現對象,
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
查看Thread 中 初始化方法,
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals){
// target 爲成員變量
this.target = target;
}
Thread 和 Ruannable 區別
Thread,是一個類,實現是 Ruannable 接口。Ruannable 是一個接口。所以繼承thread類實現多線程,其實也相當於是實現runnable接口的run方法。
java 是單繼承語言,一個類只能有一個父類,但是可以實現多個接口,Ruannable的擴展性更加好一些,避免了java中單繼承的侷限性。
多個線程基於某一個Runnable對象建立的,共享Runnable對象資源,增加程序的健壯性,實現瞭解耦操作,代碼和線程獨立。
繼承 Thread 類, run 方法實現在 Thread 子類中,thread 對象運行自己的 run 方法邏輯,創建對象以及業務邏輯沒有分離,實現Runnable 接口,run方法邏輯轉移到 Runnable 的實現類中,是調用 Runnable 實現的 run 方法邏輯,可以將創建對象和執行邏輯業務分離開來,線程控制邏輯在Thread類中,業務運行邏輯在Runnable實現類中。解耦更爲徹底
通過以上比較,推薦使用 Ruannable 接口 實現多線程 。