java創建線程的三種方式——附源碼說明

首先我們知道啓動一個線程都是通過調用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方法。

  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章