線程的創建主要有三種方式
Thread直接創建
Thread類中有run()
方法,每次線程調用start()
時,線程啓動並開始執行run()
方法。因此可以通過重寫Thread類中的run()
方法來實現線程。
Thread thread = new Thread(){
@Override
public void run() {
System.out.println("t1 run...");
}
};
thread.setName("t1");
thread.start();
System.out.println("main run...");
Runnable接口繼承實現線程
Runnable runnable = () -> System.out.println("t1 run...");
new Thread(runnable, "t1").start();
System.out.println("main run....");
Runnable接口實現線程源碼查看
首先,根據上述代碼可以看到,在main()
方法中通過向Thread
類傳入繼承了Runnable
接口的類來實現線程,然後根據這個,找到在Thread
類中運行的代碼
public Thread(Runnable target, String name) {
this(null, target, name, 0);
}
通過Thread
類中的源碼發現,在該構造方法中調用了另一個構造方法,其中Runnable對象被傳入到另一個構造方法的第二個參數的位置。然後追蹤該被調用的構造方法,如下:
public Thread(ThreadGroup group, Runnable target, String name,
long stackSize) {
this(group, target, name, stackSize, null, true);
}
依然可以看到該構造方法又調用了另一個構造方法,而Runnable對象依然在被調用的構造方法的第二個參數位置。繼續追蹤被調用的構造方法,如下:
private Thread(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
if (g == null) {
/* Determine if it's an applet or not */
/* If there is a security manager, ask the security manager
what to do. */
if (security != null) {
g = security.getThreadGroup();
}
/* If the security manager doesn't have a strong opinion
on the matter, use the parent thread group. */
if (g == null) {
g = parent.getThreadGroup();
}
}
/* checkAccess regardless of whether or not threadgroup is
explicitly passed in. */
g.checkAccess();
/*
* Do we have the required permissions?
*/
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(
SecurityConstants.SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
g.addUnstarted();
this.group = g;
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
this.target = target;
setPriority(priority);
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
this.tid = nextThreadID();
}
在上述代碼的 第53行可以看到這樣一行代碼:this.target = target;
,由此可知,該構造方法中將Runnable的對象賦給了類中的屬性。然後通過搜索Thread
類中的target
屬性,發現了Thread
類中的run()
方法(其中線程創建時,當Thread
類的start()
方法調用後,會自動執行Thread
類中的run()
方法),而run()
方法的源碼如下:
@Override
public void run() {
if (target != null) {
target.run();
}
}
通過上述代碼可以看出,在Thread
類中的run()
方法中,首先判斷target
是否存在,即Runnable
對象是否存在,如果存在,則調用Runnable
對象的run()
方法,而Runnable
接口的源碼如下:
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
因此,通過Runnable
接口創建線程時,需要實現Runnable
接口中的Run()
方法。雖然在線程創建的時候執行的是Thread
類中的run()
方法,但實際調用的還是Runnable
接口實現的run()
方法。
FutureTask類實現
FutureTask<Integer> task = new FutureTask<>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println("t1 run ....");
return 100;
}
});
new Thread(task, "t1s").start();
int num = task.get();
System.out.println("main run" + num);
注意:
- 其中
FutureTask
類實現的線程方式是可以獲取該線程的返回值的,獲取方式如上代碼通過task.get()
方式。如果FutureTask
創建的線程需要獲取返回值,但線程未運行完,即線程還未返回值時,以上述代碼爲例,其中main
線程會在獲取返回值的位置,即上述代碼中的task.get()
處等待,當main
線程獲取到返回值時,才繼續向下運行。 - 其中
FutureTask
方式創建的線程有返回值,而Thread
和Runnable
方式創建的線程沒有返回值。
FutureTask線程實現源碼查看
同樣,線程的實現都需要調用Thread
類,根據這個追蹤Thread
類的構造函數。源碼如下:
public Thread(Runnable target, String name) {
this(null, target, name, 0);
}
看到這的時候發現通過FutureTask
實現線程的方式與通過Runnable
接口實現線程的方式在Thread
類中的調用方式相同。但在上述Thread
類的構造方法中傳入的是Runnable
接口實現的對象,而在main
方法中又可以看出在Thread
類中傳入的是FutureTask
類。
FutureTask
類與Runnable
接口是什麼關係呢?根據這個疑問查看FutureTask
類的源碼:
public class FutureTask<V> implements RunnableFuture<V> {
// ......
}
根據上述代碼,可以看到FutureTask
類並沒有實現Runnable
接口,但實現了RunnableFuture
接口。(猜想:RunableFuture
接口繼承了Runnable
接口),根據這個,再查看RunnableFuture
接口的源碼, 如下:
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
根據上面的代碼是可以確認RunnableFuture
接口是繼承Runnable
接口的。
通過上面的分析,可以知道爲什麼FutureTask
類的實例可以傳入到Thread
類的實例中。但在通過FutureTask
類實現的線程中,實現了Callable
接口並通過實現call()
方法來實現該線程的邏輯,這是什麼原因?FutureTask
類實現的線程又是如何返回值的。
通過之前的源碼分析,可以知道,Thread
中的run()
方法調用的實際上是Runnable
接口的實現類的run()
方法。同時FutureTask
類實現了繼承Runnable
接口的RunnableFutrue
接口,那麼FutureTask
必然實現run()
方法,接下來查看FutureTask
類中的run()
方法實現,源碼如下:
public void run() {
if (state != NEW ||
!RUNNER.compareAndSet(this, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
通過上面代碼的第6行:Callable<V> c = callable;
可以看到將FutureTask
類中的屬性callable
賦給了c
,並且在11行調用了call()
方法,並返回了結果給result
,第19行又執行了set(result)
代碼。由此產生以下幾個疑問:
FutureTask
類中的callable
屬性的值或對象的實例是如何傳進來的?(聯想main()
方法中線程實現時在FuturtTask
類內傳入了實現的Callable
接口的實例)。- 在
FutureTask
中run()
方法並沒有返回值,那通過FutureTask
類實現的線程又是如何返回值的?(聯想main()
方法中獲取線程的返回值是通過FutureTask
類的實例調用get()
方法)
首先解決第一個疑問,callable
屬性是如何獲得值的。在main()
方法中傳入了Callable
接口的實例,根據這個查看FutureTask
的源碼,如下:
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
如代碼所示,FutureTask
類直接通過構造方法將Callable
類的實例傳給callable
屬性。
解決第二個問題,如何將線程的值返回。在FutureTask
類中的run()
方法有三行代碼Callable<V> c = callable;
,result = c.call();
,set(result);
,再根據main()
方法獲取線程的返回值是通過get()
方法,可以看出result
就是返回值,call()
是Callable
接口的方法。其中Callable
接口源碼如下:
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
set(result)
代碼是將返回值結果存儲到了FutureTask
的屬性中,可以追蹤FutureTask
類中set()
方法的源碼,如下:
protected void set(V v) {
if (STATE.compareAndSet(this, NEW, COMPLETING)) {
outcome = v;
STATE.setRelease(this, NORMAL); // final state
finishCompletion();
}
}
可以看到,set()
方法中將返回值賦給了FutureTask
類的屬性outcome
。聯想到main()
方法中通過get()
方法獲取線程返回值。然後查看FutureTask
類中的get()
方法,代碼如下:
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
可以看到最終返回的是report(s)
的方法,繼續追蹤到report()
方法,源碼如下:
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
可以看到,方法中將outcome
屬性的值賦給x
, 然後將x
返回出去。最總在main()
方法中獲得返回的值。