JAVA線程之Thread類詳解

Thread類用於操作線程,是所以涉及到線程操作(如併發)的基礎。本文將通過源碼對Thread類的功能作用進行分析。

一、屬性

    /* Make sure registerNatives is the first thing <clinit> does. */
    private static native void registerNatives();
    static {
        registerNatives();
    }

    private volatile String name;
    private int            priority;
    private Thread         threadQ;
    private long           eetop;

    /* Whether or not to single_step this thread. */
    private boolean     single_step;

    /* Whether or not the thread is a daemon thread. */
    private boolean     daemon = false;

    /* JVM state */
    private boolean     stillborn = false;

    /* What will be run. */
    private Runnable target;

    /* The group of this thread */
    private ThreadGroup group;

    /* The context ClassLoader for this thread */
    private ClassLoader contextClassLoader;

    /* The inherited AccessControlContext of this thread */
    private AccessControlContext inheritedAccessControlContext;

    /* For autonumbering anonymous threads. */
    private static int threadInitNumber;
    private static synchronized int nextThreadNum() {
        return threadInitNumber++;
    }

    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;
    /*
     * InheritableThreadLocal values pertaining to this thread. This map is
     * maintained by the InheritableThreadLocal class.
     */

    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

    /*
     * The requested stack size for this thread, or 0 if the creator did
     * not specify a stack size.  It is up to the VM to do whatever it
     * likes with this number; some VMs will ignore it.
     */
    private long stackSize;

    /*
     * JVM-private state that persists after native thread termination.
     */
    private long nativeParkEventPointer;

    /*
     * Thread ID
     */
    private long tid;

    /* For generating thread ID */
    private static long threadSeqNumber;

    /* Java thread status for tools,
     * initialized to indicate thread 'not yet started'
     */

    private volatile int threadStatus = 0;
  volatile Object parkBlocker;

    /* The object in which this thread is blocked in an interruptible I/O
     * operation, if any.  The blocker's interrupt method should be invoked
     * after setting this thread's interrupt status.
     */
    private volatile Interruptible blocker;
    private final Object blockerLock = new Object();

    public final static int MIN_PRIORITY = 1;

    /**
     * The default priority that is assigned to a thread.
     */
    public final static int NORM_PRIORITY = 5;

    /**
     * The maximum priority that a thread can have.
     */
    public final static int MAX_PRIORITY = 10;
    public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE,

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         */
        WAITING,

        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;
    }

此處重點說一下線程的優先級和狀態。

1.線程優先級


    private int  priority;

    public final static int MIN_PRIORITY = 1;
    public final static int NORM_PRIORITY = 5;
    public final static int MAX_PRIORITY = 10;

   public final void setPriority(int newPriority) {
        ThreadGroup g;
        checkAccess();
        if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
            throw new IllegalArgumentException();
        }
        if((g = getThreadGroup()) != null) {
            if (newPriority > g.getMaxPriority()) {
                newPriority = g.getMaxPriority();
            }
            setPriority0(priority = newPriority);
        }
    }

    public final int getPriority() {
        return priority;
    }

線程執行有優先級,優先級越高先執行機會越大(並不是一定先執行!!)。優先級用int的priority參數表示。
線程優先級最高爲10,最低爲1。默認爲5
2.線程的狀態

 public enum State {
        NEW,
        RUNNABLE,
        BLOCKED,
        WAITING,
        TIMED_WAITING,
        TERMINATED;
    }
      public State getState() {
        return sun.misc.VM.toThreadState(threadStatus);
    }

Thread對象共有6種狀態:NEW(新建),RUNNABLE(運行),BLOCKED(阻塞),WAITING(等待),TIMED_WAITING(有時間的等待),TERMINATED(終止);狀態轉換如下:
這裏寫圖片描述

也有一種說法,我認爲也可以:
線程只有”就緒”、”阻塞”、”運行”三種狀態(新建[NEW]”和”終止[TERMINATED]”狀態的線程並不是線程,只是代表一個線程對象還存在):
1. RUNNABLE,對應”就緒”和”運行”兩種狀態,也就是說處於就緒和運行狀態的線程在java.lang.Thread中都表現爲”RUNNABLE”
2. BLOCKED,對應”阻塞”狀態,此線程需要獲得某個鎖才能繼續執行,而這個鎖目前被其他線程持有,所以進入了被動的等待狀態,直到搶到了那個鎖,纔會再次進入”就緒”狀態
3. WAITING,對應”阻塞”狀態,代表此線程正處於無限期的主動等待中,直到有人喚醒它,它纔會再次進入就緒狀態
4. TIMED_WAITING,對應”阻塞”狀態,代表此線程正處於有限期的主動等待中,要麼有人喚醒它,要麼等待夠了一定時間之後,纔會再次進入就緒狀態

二、構造器

    public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }

    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

    public Thread(ThreadGroup group, Runnable target) {
        init(group, target, "Thread-" + nextThreadNum(), 0);
    }

   public Thread(String name) {
        init(null, null, name, 0);
    }

    public Thread(ThreadGroup group, String name) {
        init(group, null, name, 0);
    }

    public Thread(Runnable target, String name) {
        init(null, target, name, 0);
    }

    public Thread(ThreadGroup group, Runnable target, String name) {
        init(group, target, name, 0);
    }


    public Thread(ThreadGroup group, Runnable target, String name,
                  long stackSize) {
        init(group, target, name, stackSize);
    }
     private void init(ThreadGroup g, Runnable target, String name,long stackSize) {
        init(g, target, name, stackSize, null, true);
    }
    private void init(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 doesn't have a strong opinion of the matter
               use the parent thread group. */
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }

        g.checkAccess();

        if (security != null) {
            if (isCCLOverridden(getClass())) {
                security.checkPermission(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 */
        tid = nextThreadID();
    }

通過以上代碼可以看出,Thread()對外提供了8個構造器,但是都是接收不同參數,然後調用init(ThreadGroup g, Runnable target, String name, long stackSize)方法。故我們只需分析次init()方法即可。
init()方法共有4個參數,分別代表:

  1. ThreadGroup g 指定當前線程的線程組,未指定時線程組爲創建該線程所屬的線程組。線程組可以用來管理一組線程,通過activeCount() 來查看活動線程的數量。其他沒有什麼大的用處。 
  2. Runnable target 指定運行其中的Runnable,一般都需要指定,不指定的線程沒有意義,或者可以通過創建Thread的子類並重新run方法。
  3. String name 線程的名稱,不指定自動生成。
  4. long stackSize 預期堆棧大小,不指定默認爲0,0代表忽略這個屬性。與平臺相關,不建議使用該屬性。

三、public方法

Thread Thread.currentThread() :獲得當前線程的引用。獲得當前線程後對其進行操作。
Thread.UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() :返回線程由於未捕獲到異常而突然終止時調用的默認處理程序。
int Thread.activeCount():當前線程所在線程組中活動線程的數目。
void dumpStack() :將當前線程的堆棧跟蹤打印至標準錯誤流。
int enumerate(Thread[] tarray) :將當前線程的線程組及其子組中的每一個活動線程複製到指定的數組中。
Map<Thread,StackTraceElement[]> getAllStackTraces() :返回所有活動線程的堆棧跟蹤的一個映射。
boolean holdsLock(Object obj) :當且僅當當前線程在指定的對象上保持監視器鎖時,才返回 true。
boolean interrupted() :測試當前線程是否已經中斷。
void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh) :設置當線程由於未捕獲到異常而突然終止,並且沒有爲該線程定義其他處理程序時所調用的默認處理程序。
void sleep(long millis) :休眠指定時間
void sleep(long millis, int nanos) :休眠指定時間
void yield() :暫停當前正在執行的線程對象,並執行其他線程。意義不太大

void checkAccess() :判定當前運行的線程是否有權修改該線程。
ClassLoader getContextClassLoader() :返回該線程的上下文 ClassLoader。
long getId() :返回該線程的標識符。
String getName() :返回該線程的名稱。
int getPriority() :返回線程的優先級。
StackTraceElement[] getStackTrace() :返回一個表示該線程堆棧轉儲的堆棧跟蹤元素數組。
Thread.State getState() :返回該線程的狀態。
ThreadGroup getThreadGroup() :返回該線程所屬的線程組。
Thread.UncaughtExceptionHandler getUncaughtExceptionHandler() :返回該線程由於未捕獲到異常而突然終止時調用的處理程序。
void interrupt() :中斷線程。
boolean isAlive() :測試線程是否處於活動狀態。
boolean isDaemon() :測試該線程是否爲守護線程。
boolean isInterrupted():測試線程是否已經中斷。
void join() :等待該線程終止。
void join(long millis) :等待該線程終止的時間最長爲 millis 毫秒。
void join(long millis, int nanos) :等待該線程終止的時間最長爲 millis 毫秒 + nanos 納秒。
void run() :線程啓動後執行的方法。
void setContextClassLoader(ClassLoader cl) :設置該線程的上下文 ClassLoader。
void setDaemon(boolean on) :將該線程標記爲守護線程或用戶線程。
void start():使該線程開始執行;Java 虛擬機調用該線程的 run 方法。
String toString():返回該線程的字符串表示形式,包括線程名稱、優先級和線程組。

在此重點是Thread currentThread(),void run() ,void start(),boolean interrupted() ,void interrupt(),boolean isInterrupted(),void sleep(long millis) ,void sleep(long millis, int nanos) ,void join() ,
void join(long millis) ,void join(long millis, int nanos) 這幾個方法
1.Thread currentThread()
該方法是本地靜態方法,用於獲取當前線程,返回線程對象。

   public static native Thread currentThread();

2.void run()
是線程執行任務的主要代碼。

   public void run() {
        if (target != null) {
            target.run();
        }
    }

當target不爲空時,執行target的run方法。若target爲空,則需要重寫此方法,方法內是業務邏輯
3.void start()
使得該線程開始執行;Java 虛擬機調用該線程的 run 方法。

  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) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

    private native void start0();

4.boolean interrupted() & void interrupt() & boolean isInterrupted()
這三個方法是針對線程中斷標記的方法,

    public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }
     public boolean isInterrupted() {
        return isInterrupted(false);
    }
    public void interrupt() {
        if (this != Thread.currentThread())
            checkAccess();

        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                interrupt0();           // Just to set the interrupt flag
                b.interrupt(this);
                return;
            }
        }
        interrupt0();
    }

    private native boolean isInterrupted(boolean ClearInterrupted);

interrupt():中斷本線程(將中斷狀態標記爲true)
isInterrupted():檢測本線程是否已經中斷 。如果已經中斷,則返回true,否則false。中斷狀態不受該方法的影響。 如果中斷調用時線程已經不處於活動狀態,則返回false。
interrupted():檢測當前線程是否已經中斷 。如果當前線程存在中斷,返回true,並且修改標記爲false。再調用isIterruoted()會返回false。如果當前線程沒有中斷標記,返回false,不會修改中斷標記。
測試代碼:

public class TestThread {
    public static void main(String[] args) throws Exception{
        test();
    }

    public static void test() throws Exception{
        Thread t1 = new Thread(new MyThread());
        Thread t2 = new Thread(new MyThread());
        t1.start();
        t1.interrupt();
        System.out.println("1"+t1.isInterrupted());
        System.out.println("2"+t1.isInterrupted());
        System.out.println("3"+t1.interrupted());
        t2.start();

    }

}

class MyThread extends Thread {

    @Override
    public void run() {

        for (int i = 0; i < 10000; i++) {

        }
        System.out.println("4"+currentThread().getName()+interrupted());
        System.out.println("5"+currentThread().getName()+interrupted());

    }
}

執行結果:

1true
4Thread-1true
2false
5Thread-1false
3false
4Thread-3false
5Thread-3false

5.void sleep(long millis) & void sleep(long millis, int nanos)
這兩個方法是讓線程休眠

 public static void sleep(long millis, int nanos) throws InterruptedException {
        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException("nanosecond timeout value out of range");
        }

        if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
            millis++;
        }

        sleep(millis);
    }
   public static native void sleep(long millis) throws InterruptedException;

如上,兩個參數的sleep只是將nanos轉爲millis(四捨五入轉換),調用sleep(millis)方法。
sleep(millis)是本地方法,讓當前線程休眠指定時間。
sleep不釋放鎖。
6.void join() & void join(long millis) & void join(long millis, int nanos)
這三個方法是讓等待該線程終止。

    public final void join() throws InterruptedException {
        join(0);
    }
    public final synchronized void join(long millis, int nanos)throws InterruptedException {

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
            millis++;
        }

        join(millis);
    }
   public final synchronized void join(long millis)throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

當傳入參數爲0時,代表一直等待到結束。

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