多線程的實現方式:
多線程的實現方式,目前有4中: Thread、Runnable、Callable、線程池【本文不介紹】
- Thread類 :通過繼承Thread類,並重寫其中的Run()方法,調用Thread.start() 方法啓動線程
- Runnable接口:實現Runnable接口,並且完成Run()方法,仍需借用Thread.start()方法啓動線程
- Callable接口: 通過實現接口,重寫call()方法【有返回值】,通過 FutureTask裝飾,最終也是需要Thread.start()方法啓動線程
代碼示例:
- 繼承Thread :
public class ThreadTest extends Thread{
public static void main(String[] args) {
ThreadTest threadTest = new ThreadTest();
threadTest.start();
}
@Override
public void run() {
System.out.println("I am Thread! ");
}
}
運行結果:
I am Thread!
- 實現Runnable 接口 :
public class RunnableTest implements Runnable {
public static void main(String[] args) {
RunnableTest runnableTest = new RunnableTest();
new Thread(runnableTest).start();
}
@Override
public void run() {
System.out.println("I am Runnable");
}
}
運行結果:
I am Runnable
- Callable接口實現多線程需要藉助 FutureTask 類:
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CallableTest implements Callable {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CallableTest callableTest = new CallableTest();
FutureTask<String> task = new FutureTask<String>(callableTest);
new Thread(task).start();
System.out.println(task.get());
}
@Override
public Object call() throws Exception {
return "I am CallableTest !";
}
}
運行結果:
I am CallableTest!
源碼解析:
關於Thread類 和 Runnable接口,我們可以看到:
- Runnable 中 :
public interface Runnable {
// 定義了Run()方法,執行入口,業務邏輯均在此方法中實現
public abstract void run();
- 在Thread中:
public class Thread implements Runnable {
// Runnable的實現類, 上面的實例代碼 runnableTest 對象
private Runnable target;
// 線程啓動方法
public synchronized void start() {
// 狀態校驗 0:NEW 新建狀態
if (threadStatus != 0)
throw new IllegalThreadStateException();
// 添加進線程組 -main 方法中, 後續調用
group.add(this);
boolean started = false;
try {
//調用native方法執行線程run方法
start0();
started = true;
} finally {
try {
if (!started) {
// 啓動失敗,從線程組中移除當前前程
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
// 原生的方法
private native void start0();
// 當多線程的實現方式實現Runnable 接口時,通過 thread.run() 方法調用 runnableTest.run()方法
public void run() {
if (target != null) {
target.run();
}
}
// 省略代碼
}
- 我們可以看到不管是Thread 還是 Runnable 線程的真正實現都是有Thread.start() 方法啓用的。
再調用 原生start0() , 回調到 run() 方法中【Runnable 通過thread.run() 方法調用 target.run(),實現我們的業務邏輯】 - Thread 和 Runnable 的區別就在於,Java 是單繼承 和多實現,使用Thread 類 難免會對我們的業務擴展產生影響。
- 面試中還有一個常問的問題:如果一個線程多次Start 後的結果 , 答案就在於 if (threadStatus != 0) throw new IllegalThreadStateException() 這兩代碼,每次啓動時都會判斷 當前 thread 的運行狀態,上次的start 會使得 threadStatus != 0 繼而拋出異常。
關於FutureTask類 和 Callable接口實現的多線程:
- Callable 接口
public interface Callable<V> {
// 計算結果,如果無法執行,則引發異常
V call() throws Exception;
}
- FutureTask 繼承關係:
FutureTask 部分源碼:
public class FutureTask<V> implements RunnableFuture<V> {
// 實現 Runnable run() 方法
public void run() {
// 判斷當前線程的狀態
if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
// 執行 Call 方法,並獲得返回結果
result = c.call();
ran = true;
} catch (Throwable ex) {
// call 方法拋出異常
result = null;
ran = false;
setException(ex);
}
// 執行成功將 返回結果放到 outcome 中,在通過get()獲得
if (ran)
set(result);
}
} finally {
runner = null;
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
// 等待線程結束,獲得線程的返回值 outcome
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
// 等待線程執行結束,或者執行異常
s = awaitDone(false, 0L);
return report(s);
}
// 等待線程執行結束,或者執行異常 ,並返回線程的狀態
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
final long deadline = timed ? System.nanoTime() + nanos : 0L;
WaitNode q = null;
boolean queued = false;
// 自旋等待線程執行結束
for (;;) {
// 線程被中斷
if (Thread.interrupted()) {
removeWaiter(q);
throw new InterruptedException();
}
int s = state;
// 執行結束返回
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
}
else if (s == COMPLETING) // cannot time out yet
Thread.yield();
else if (q == null)
q = new WaitNode();
else if (!queued)
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
q.next = waiters, q);
else if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
removeWaiter(q);
return state;
}
LockSupport.parkNanos(this, nanos);
}
else
LockSupport.park(this);
}
}
}
- 結合 Callable 和 FutureTask 源碼不難發現, FutureTask 本身是實現了Runnable 接口,已經幫我們實現了多線程功能,我們定義的 CallableTest 作爲一個 變量 賦值 FutureTask#callable 參數中,
- 在執行 Thread#start() 方法中,會調用 FutureTask#run() 方法,再到我們的 callable.call() 方法,進入到我們的業務,並把返回值賦值給 outCome。
- 當我們通過 task.get() 回去返回值時,通過等待 線程執行結束,獲得 call() 的返回值並返回。
- 總而言之, FutureTask 是個已經寫好了的線程對象,我僅需吧 Callable交給他執行,即可