首先我們知道啓動一個線程都是通過調用Thread類的start()來開啓一個新線程,那麼我們就來一起看看Thread類的start()方法源碼:
/** * Causes this thread to begin execution; the Java Virtual Machine * calls the <code>run</code> method of this thread. * <p> * The result is that two threads are running concurrently: the * current thread (which returns from the call to the * <code>start</code> method) and the other thread (which executes its * <code>run</code> method). * <p> * It is never legal to start a thread more than once. * In particular, a thread may not be restarted once it has completed * execution. * * @exception IllegalThreadStateException if the thread was already * started. * @see #run() * @see #stop() */ public synchronized void start() { /** * This method is not invoked for the main method thread or "system" * group threads created/set up by the VM. Any new functionality added * to this method in the future may have to also be added to the VM. * * A zero status value corresponds to state "NEW". */ if (threadStatus != 0) throw new IllegalThreadStateException(); /* Notify the group that this thread is about to be started * so that it can be added to the group's list of threads * and the group's unstarted count can be decremented. */ group.add(this); boolean started = false; try { 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();
start方法做了兩件事:1、將此線程加入線程組中,2、啓動這個線程。由於開啓線程是通過start0()這個native方法,我們看不到裏面的細節,但是通過註釋 —— the Java Virtual Machine calls the run method of this thread —— 我們可以知道run方法其實就相當於是個job,線程就是執行這個job的程序,專門爲run方法服務的。所以後面三種方式不管那種其實都是圍繞着run方法進行實現這個線程的“job”。
1、通過繼承Thread類,覆蓋run方法創建線程
這種方式最粗暴簡潔,直接給這個線程分配了“job”。
2、通過傳入Runnable類型對象創建線程。
1 public Thread(Runnable target) { 2 init(null, target, "Thread-" + nextThreadNum(), 0); 3 } 4 5 private void init(ThreadGroup g, Runnable target, String name, 6 long stackSize) { 7 init(g, target, name, stackSize, null, true); 8 } 9 10 11 /** 12 * Initializes a Thread. 13 * 14 * @param g the Thread group 15 * @param target the object whose run() method gets called 16 * @param name the name of the new Thread 17 * @param stackSize the desired stack size for the new thread, or 18 * zero to indicate that this parameter is to be ignored. 19 * @param acc the AccessControlContext to inherit, or 20 * AccessController.getContext() if null 21 * @param inheritThreadLocals if {@code true}, inherit initial values for 22 * inheritable thread-locals from the constructing thread 23 */ 24 private void init(ThreadGroup g, Runnable target, String name, 25 long stackSize, AccessControlContext acc, 26 boolean inheritThreadLocals) { 27 if (name == null) { 28 throw new NullPointerException("name cannot be null"); 29 } 30 31 this.name = name; 32 33 Thread parent = currentThread(); 34 SecurityManager security = System.getSecurityManager(); 35 if (g == null) { 36 /* Determine if it's an applet or not */ 37 38 /* If there is a security manager, ask the security manager 39 what to do. */ 40 if (security != null) { 41 g = security.getThreadGroup(); 42 } 43 44 /* If the security doesn't have a strong opinion of the matter 45 use the parent thread group. */ 46 if (g == null) { 47 g = parent.getThreadGroup(); 48 } 49 } 50 51 /* checkAccess regardless of whether or not threadgroup is 52 explicitly passed in. */ 53 g.checkAccess(); 54 55 /* 56 * Do we have the required permissions? 57 */ 58 if (security != null) { 59 if (isCCLOverridden(getClass())) { 60 security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); 61 } 62 } 63 64 g.addUnstarted(); 65 66 this.group = g; 67 this.daemon = parent.isDaemon(); 68 this.priority = parent.getPriority(); 69 if (security == null || isCCLOverridden(parent.getClass())) 70 this.contextClassLoader = parent.getContextClassLoader(); 71 else 72 this.contextClassLoader = parent.contextClassLoader; 73 this.inheritedAccessControlContext = 74 acc != null ? acc : AccessController.getContext(); 75 this.target = target; 76 setPriority(priority); 77 if (inheritThreadLocals && parent.inheritableThreadLocals != null) 78 this.inheritableThreadLocals = 79 ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); 80 /* Stash the specified stack size in case the VM cares */ 81 this.stackSize = stackSize; 82 83 /* Set thread ID */ 84 tid = nextThreadID(); 85 }
通過上面的源碼我們可以看到,當你用 Thread t = new Thread(runnable); 創建線程時會調用Thread類的init方法,最關鍵的是第75行,將傳進來的runnable對象賦給Thread類的target屬性,這個時候再看看我們的“job”——也就是run方法:
1 /** 2 * If this thread was constructed using a separate 3 * <code>Runnable</code> run object, then that 4 * <code>Runnable</code> object's <code>run</code> method is called; 5 * otherwise, this method does nothing and returns. 6 * <p> 7 * Subclasses of <code>Thread</code> should override this method. 8 * 9 * @see #start() 10 * @see #stop() 11 * @see #Thread(ThreadGroup, Runnable, String) 12 */ 13 @Override 14 public void run() { 15 if (target != null) { 16 target.run(); 17 } 18 }
如果target不爲null,那麼我們就執行target的run方法,這種方式就相當於job由用戶寫好,然後交給線程去執行。
3、通過傳入FutureTask類型對象創建線程
FutureTask實現了Runnable接口,所以傳入FutureTask類型對象和傳入Runnable類型的對象本質上原理一樣,只不過FutureTask可以看作是加強版的Runnable,多了一個存儲返回值和獲取返回值的功能。
既然FutureTask實現了Runnable接口,那麼一定實現了run方法,直接上源碼:
1 public void run() { 2 if (state != NEW || 3 !UNSAFE.compareAndSwapObject(this, runnerOffset, 4 null, Thread.currentThread())) 5 return; 6 try { 7 Callable<V> c = callable; 8 if (c != null && state == NEW) { 9 V result; 10 boolean ran; 11 try { 12 result = c.call(); 13 ran = true; 14 } catch (Throwable ex) { 15 result = null; 16 ran = false; 17 setException(ex); 18 } 19 if (ran) 20 set(result); 21 } 22 } finally { 23 // runner must be non-null until state is settled to 24 // prevent concurrent calls to run() 25 runner = null; 26 // state must be re-read after nulling runner to prevent 27 // leaked interrupts 28 int s = state; 29 if (s >= INTERRUPTING) 30 handlePossibleCancellationInterrupt(s); 31 } 32 }
run方法主要是執行Callable對象的call方法(上方源碼第12行),接受call方法的返回值,並調用set方法將result存儲起來。
/** * Sets the result of this future to the given value unless * this future has already been set or has been cancelled. * * <p>This method is invoked internally by the {@link #run} method * upon successful completion of the computation. * * @param v the value */ protected void set(V v) { if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { outcome = v; UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state finishCompletion(); } }
set方法主要是將返回值賦給outcome屬性,當調用FutureTask的get方法時,會先判定線程是否正常結束,如果正常結束會將outcome返回。否則會拋出異常。
1 /** 2 * Creates a {@code FutureTask} that will, upon running, execute the 3 * given {@code Callable}. 4 * 5 * @param callable the callable task 6 * @throws NullPointerException if the callable is null 7 */ 8 public FutureTask(Callable<V> callable) { 9 if (callable == null) 10 throw new NullPointerException(); 11 this.callable = callable; 12 this.state = NEW; // ensure visibility of callable 13 }
callable對象是創建FutureTask對象時傳進來的,所以這種方式的“job”來自Callable對象的call方法。