在多線程編程中,如果要使用線程來執行任務,那麼最簡單的方式就是使用Thread類來建立一個線程,當然也可以使用線程池的方式。
線程是在進程中執行的單位,線程的資源開銷相對於進程的開銷是相對較少的,所以我們一般建立線程執行,而不是進程執行。
本文不是學習Thread的使用,而是通過Thread類來一探線程從建立到結束的過程。 繼承體系
Thread類實現了Runnable介面,因此Thread不僅是一個線程類,也是一個特殊的執行任務類。 資料結構
//線程名稱的位元組數組 private volatile char name[]; //線程優先順序 private int priority; private Thread threadQ; private long eetop; //線程是否單步 private boolean single_step; //是否是守護線程 private boolean daemon = false; /* JVM state */ private boolean stillborn = false; // 要執行的run方法的對象 private Runnable target; // 這個線程的線程組 private ThreadGroup group; /* The context ClassLoader for this thread */ // 這個線程的上下文類載入器 private ClassLoader contextClassLoader; /* The inherited AccessControlContext of this thread */ private AccessControlContext inheritedAccessControlContext; //線程編號 private static int threadInitNumber; /* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ // ThreadLocal相關 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. */ // 給這個線程設定的棧的大小,預設為0 private long stackSize; /* * JVM-private state that persists after native thread termination. */ private long nativeParkEventPointer; //線程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; /** * The argument supplied to the current call to * java.util.concurrent.locks.LockSupport.park. * Set by (private) java.util.concurrent.locks.LockSupport.setBlocker * Accessed using java.util.concurrent.locks.LockSupport.getBlocker */ 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; // 用於blocker 的鎖對象 private final Object blockerLock = new Object(); /** * The minimum priority that a thread can have. */ //最低優先順序 public final static int MIN_PRIORITY = 1; /** * The default priority that is assigned to a thread. */ // 線程預設的執行優先順序為 5 public final static int NORM_PRIORITY = 5; /** * The maximum priority that a thread can have. */ // 線程執行的最高的優先順序為 10 public final static int MAX_PRIORITY = 10;
上面的屬性包含了線程的基本屬性,Thread 並不是一個執行任務,而是作為一個線程單位存在,其內部擁有可執行任務 target(Runnable 類型),這個我們在後面的方法中將會更加細緻的探討。
線程的優先順序在不同的平台上,對應的系統優先順序會不同,可能多個優先順序對應同一個系統優先順序,優先順序高的線程並不一定優先執行,這個由JVM來解釋並向系統提供參考。 線程狀態(生命週期)
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 */ BLOCKED, /** * Thread state for a waiting thread. */ WAITING, /** * Thread state for a waiting thread with a specified waiting time. */ TIMED_WAITING, /** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED;}
線程的狀態有NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED,官方文檔說得也很詳細。 構造方法
1、無參構造
public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0);}
2、指定執行任務
public Thread(Runnable target) { init(null, target, "Thread-" + nextThreadNum(), 0);}
3、指定線程組和執行任務
public Thread(ThreadGroup group, Runnable target) { init(group, target, "Thread-" + nextThreadNum(), 0);}
還有其它很多的構造方法,這裡就不一一羅列出來了,在構造方法中,都調用了init 方法來進行初始化。 init 方法
private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc) { if (name == null) { throw new NullPointerException("name cannot be null"); } // 設定線程名稱 this.name = name.toCharArray(); //當前調用線程為 父線程 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(); } } /* 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(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 (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 */ // 設定線程id,nextThreadID()進行了同步處理 tid = nextThreadID();}
構造方法是被調用者線程執行的,因此調用者線程將作為父線程,新建立的線程繼承父線程的部分屬性。 run 方法
Thread繼承Runnable 介面,那麼就需要實現run 方法。
public void run() { if (target != null) { target.run(); }}
Thread中的run 方法調用的還是目標任務的run 方法,通過直接調用run 方法並不會建立線程來單獨執行任務,這個我們通過Thread中的run 也可以看出來,要建立線程來執行任務,需要調用Thread的start 方法。 start 方法
/** * Causes this thread to begin execution; the Java Virtual Machine * calls the run method of this thread. */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方法是同步的(synchronized 修飾),在啟動線程前會檢查線程狀態,如果線程不是初始狀態,那麼會拋出異常,因此多次調用start 是不可行的。
start 方法被調用者線程執行,JVM將會調用這個線程的run方法,這樣產生的結果是,兩個線程執行著,其中一個是調用start()方法的線程執行,另一個線程是執行run方法的線程。 sleep()方法
/** * Causes the currently executing thread to sleep (temporarily cease * execution) for the specified number of milliseconds plus the specified * number of nanoseconds, subject to the precision and accuracy of system * timers and schedulers. The thread does not lose ownership of any * monitors. */ 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); // 調用本地sleep 方法 }
sleep方法的作用使得當前線程休眠一定的時間,值得注意的是這個期間是不會釋放持有的鎖。
###join()方法
/** * Waits at most {@code millis} milliseconds for this thread to * die. A timeout of {@code 0} means to wait forever. */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; } } }
調用線程通過join方法等待被調用線程任務執行,直到逾時或者終止。 interrupt()方法
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(); //調用本地方法}
interrupt()方法是中斷當前的線程(設定中斷標誌位),線程當檢測到中斷標誌位被設定後,可能會拋出InteruptedExeption, 同時會清除線程的中斷狀態,通常中斷可以作為取消任務的一種比較安全的方式(前提是能響應中斷)。 總結
通過對源碼的簡單分析,瞭解了Thread一些使用方法和注意事項,整個過程對Thread的分析是比較淺顯的,當然對多線程的研究也才剛剛開始,並沒有完全深入各個細節,但是通過源碼的分析,知道了線程整個建立的大致過程,對於使用Thread也會更加得心應手。