入坑兩個月,java.lang包下的Thread類
Thread類API中的英文描述:
file:///D:/Java/JDK8/Docs/docs/api/index.html
英語不好,大致的意思是:
線程是程序執行時的線程,java虛擬機(JVM)允許一個應用運行多個線程(並發)。
每一個線程都自己的優先權,優先級高的線程會比優先級低的線程優先執行。每一個線程可以被設置成守護線程(daemon),當一段代碼運行一些線程時,會創建一個Thread類的對象,這個新的線程初始的優先級是創建這個線程對象時程序員設定好的,僅且僅當用戶把這個線程設定成daemon,這個線程才是daemon
當java虛擬機(JVM)開啟時,這兒通常有一個非daemon線程(一般的會調用某個類的main方法),java虛擬機(JVM)會一直執行這些線程,直到出現下面幾種情況:
第一種:程序調用Runtime類的退出方法,並且安全管理器允許該退出操作
第二種:除了daemon線程外的其他所有線程執行完畢(處於dead狀態);線程run方法中遇到return語句;線程拋出一個run方法無法處理的異常
有兩種方法來創建一個新的線程。
第一種:聲明一個類,該類繼承Thread,該類應該重寫Thread類的run方法,通過調用start方法來啟動該線程,下面給出一個列子:
1 class PrimeThread extends Thread { 2 long minPrime; 3 PrimeThread(long minPrime) { 4 this.minPrime = minPrime; 5 } 6 7 public void run() { 8 // compute primes larger than minPrime 9 . . . 10 } 11 }
第二種:聲明一個類,實現Runnable接口,重寫Runnable接口中的run方法,下面也給出一個例子:
1 class PrimeRun implements Runnable { 2 long minPrime; 3 PrimeRun(long minPrime) { 4 this.minPrime = minPrime;5 } 6 public void run() { 7 // compute primes larger than minPrime 8 . . . 9 } 10 }
下面我們來看一下Thread的源代碼部分:
1. Thread類實現了Runnable接口:
public class Thread implements Runnable
2. Thread類的構造器:
1 public Thread() 2 public Thread(Runnable target) 3 Thread(Runnable target, AccessControlContext acc) 4 public Thread(ThreadGroup group, Runnable target) 5 public Thread(String name) 6 public Thread(ThreadGroup group, String name) 7 public Thread(Runnable target, String name) 8 public Thread(ThreadGroup group, Runnable target, String name) 9 public Thread(ThreadGroup group, Runnable target, String name, long stackSize)
Thread類一共有9個構造器。其中第3個構造器沒有public修飾,默認用default修飾,同一個包下可用,不多做說明。
通過上面9個構造器(除3)可以看出,用戶在創建一個Thread類的對象時,可以設定的參數有:ThreadGroup、Runnable、name、stackSize
ThreadGroup:是java.lang包下的一個ThreadGroup類,ThreadGroup對象表示一個線程的集合,也可以包含另一個ThreadGroup對象。
Thread類init方法關於ThreadGroup部分源碼:
1 Thread parent = currentThread(); 2 SecurityManager security = System.getSecurityManager(); 3 if (g == null) { 4 /* Determine if it‘s an applet or not */ 5 6 /* If there is a security manager, ask the security 7 manager what to do. */ 8 if (security != null) { 9 g = security.getThreadGroup(); 10 } 11 12 /* If the security doesn‘t have a strong opinion of the 13 matter use the parent thread group. */ 14 if (g == null) { 15 g = parent.getThreadGroup(); 16 } 17 } 18 19 /* checkAccess regardless of whether or not threadgroup 20 is explicitly passed in. */ 21 g.checkAccess();
其中currentThread()方法是獲取當前運行的線程,下面寫段代碼做個試驗:
1 public class Demo7{ 2 public static void main(String[] args){ 3 Thread t = Thread.currentThread(); 4 System.out.println(t.getName()); 5 } 6 }
System.getSecurityManager()是System類中的一個靜態方法,該方法是用來返回一個SecurityManager類的對象security,下面是System類中getSecurityManager():
/** * Gets the system security interface. * * @return if a security manager has already been established for the * current application, then that security manager is returned; * otherwise, <code>null</code> is returned. * @see #setSecurityManager */ public static SecurityManager getSecurityManager() { return security; }
註釋說明,如果這個安全管理已經創建security對象,則返回這個security,如果沒有,則返回null,其中System類的security初始值被設置為null。
/* The security manager for the system. */ private static volatile SecurityManager security = null;
再看Thread類中init方法關於ThreadGroup部分做了什麽處理?
先創建兩個對象parent和security:
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
接著對用戶創建Thread類對象設置的參數做一個判斷,如果用戶沒有設定ThreadGroup參數,則傳遞一個null值
init方法先判斷這個參數是否為null,如果不為null,則判斷security是否為空,如果不為空,則獲取security所在的ThreadGroup賦值給用戶創建的Thread對象,即g的值
接著再判斷g是否為空,如果還為null,則將當前線程的ThreadGroup賦值給g。總之,如果用戶未設置g值,就把security的g值賦值給g,如果security的g值也為空,就把parent的g值賦給g。
最後再調用g(ThreadGroup)的checkAccess方法,ThreadGroup類的checkAccess方法源碼:
1 public final void checkAccess() { 2 SecurityManager security = System.getSecurityManager(); 3 if (security != null) { 4 security.checkAccess(this); 5 } 6 }
可以看出該方法,其實是調用了SecurityManager對象的checkAccess(ThreadGroup g)方法:
1 public void checkAccess(ThreadGroup g) { 2 if (g == null) { 3 throw new NullPointerException("thread group can‘t be null"); 4 } 5 if (g == rootGroup) { 6 checkPermission(SecurityConstants.MODIFY_THREADGROUP_PERMISSION); 7 } else { 8 // just return 9 } 10 }
其中rootGroup應該是g的最高級parent(未必正確),看一下源碼:
1 private static ThreadGroup rootGroup = getRootGroup(); 2 private static ThreadGroup getRootGroup() { 3 ThreadGroup root = Thread.currentThread().getThreadGroup(); 4 while (root.getParent() != null) { 5 root = root.getParent(); 6 } 7 return root; 8 }
checkPermission(SecurityConstants.MODIFY_THREADGROUP_PERMISSION)這個坑之後再填吧,挖不下去了。
Runnable:前面介紹了創建Thread類對象的兩種方法,其中一種就是傳入一個Runnable對象。
如果在創建一個線程時,沒有傳入Runnable對象,則init方法默認傳入的是一個null值,來看一下源碼:
public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); }
在看看init方法對應參數代表什麽意思:
private void init(ThreadGroup g, Runnable target, String name, long stackSize) { init(g, target, name, stackSize, null, true); }
從中還可以看出,如果在創建Thread類對象時,沒有指定其姓名,會默認設置名字,即Thread-加上nextThreadNum()返回值:
private static int threadInitNumber; private static synchronized int nextThreadNum() { return threadInitNumber++; }
所以第一個線程的名字會默認為Thread-0,第二個線程的名字為Thread-1......
stackSize:棧的大小,待補充......
3. 線程的狀態
Thread類有個內部枚舉類State,該類就聲明了線程的幾種狀態:
public enum State { NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED; }
NEW:線程剛創建時具有的狀態;
public class Demo8{ public static void main(String[] args){ Thread t = new Thread(); System.out.println(t.getState()); } }
RUNNABLE:是指線程正在運行,線程獲取CPU的時間片
public class Demo8{ public static void main(String[] args){ Thread t = new Thread(); t.start(); System.out.println(t.getState()); } }
BLOCKED:是指程序進入阻塞狀態,假如有兩個線程,並且兩個線程都是同步安全的(synchronized),當一個線程處於runnable狀態時,則另一個線程處於blocked狀態。
public class Demo8{ public static void main(String[] args){ Thread t1= new Thread(){ public void run(){ synchronized(Thread.class){ for(int i = 0; i < 100; i++){ } } } }; Thread t2 = new Thread(){ public void run(){ synchronized(Thread.class){ System.out.println(t1.getState()); } } }; t1.setPriority(1); t2.setPriority(10); t1.start(); t2.start(); } }
WAITING:程序處於等待狀態,調用wait()、join()、await()、lock()等方法,都會使線程處於waiting狀態,需要註意的是這些方法必須是無參數的。
public class Demo8{ public static void main(String[] args){ Thread t1 = new Thread(){ public void run(){ try{ join(); } catch(InterruptedException e){ e.printStackTrace(); } } }; Thread t2 = new Thread(){ public void run(){ System.out.println(t1.getState()); } }; t1.start(); t2.start(); } }
TIMED_WAITING:程序處於限時等待狀態,調用wait()、join()、await()、lock()、sleep()等方法,都會使線程處於waiting狀態,需要註意的是這些方法必須加入參數。
TERMINATED:終止狀態,即線程結束
4. Thread類中部分方法源碼解析
————this.start()方法
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) { } } }
可以看出線程是在調用start方法時加入init方法中指定的線程池的,其次線程在創建時並不是把內部的枚舉類State的NEW值給這個線程,而是定義一個int型的threadStatus
變量,並且這個變量初始值為0。
private volatile int threadStatus = 0;
並且start還調用了一個本地的start0()方法,由於沒看過JVM相關的知識,所以對於native修飾的方法無能無力:
private native void start0();
不過IBM有一篇文章對此講解的比較詳細,這裏附上鏈接:https://www.ibm.com/developerworks/cn/java/j-lo-processthread/#icomments
大概的意思是java的start方法會調用 JVM_StartThread方法,而 JVM_StartThread方法會創建一個與本地相關的線程,該線程與java創建的線程有著一一對應關系。
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread)) … native_thread = new JavaThread(&thread_entry, sz); …
這篇文章並沒有詳細講解JVM的代碼實現過程,而是給出了方法調用圖,該圖來自上面鏈接的那篇文章:
————setPriority(int)方法
這裏先留坑,簡單講一下,線程的優先級可以理解為線程搶占cpu時間片的概率,因此並不能保證優先級高一定會先執行
1 public class Demo2 { 2 public static void main(String[] args) { 3 Thread t1 = new Thread() { 4 @Override 5 public void run() { 6 for(int i = 0; i < 1000; i++) { 7 System.out.println("********"); 8 } 9 } 10 }; 11 Thread t2 = new Thread() { 12 @Override 13 public void run() { 14 for(int i = 0; i < 1000; i++) { 15 System.out.println("--------"); 16 } 17 } 18 }; 19 t1.setPriority(1); 20 t2.setPriority(10); 21 t1.start(); 22 t2.start(); 23 } 24 }
其次,windows操作系統的優先級為7個級別(未驗證),而java的優先級分為10個級別,所以java程序1~10幾個優先級中,必定有幾個優先級在windows操作系統下級別是一樣的,真正決定優先級的應該是本地的setPriority0()方法。
setPriority0(priority = newPriority);
—————activeCount方法,獲取當前線程池中的線程數量
1 import java.lang.reflect.InvocationTargetException; 2 import java.lang.reflect.Method; 3 public class Demo3 { 4 public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { 5 MyRunnable m = new MyRunnable(); 6 Thread t1 = Thread.currentThread(); 7 ThreadGroup tg = t1.getThreadGroup(); 8 Thread t2 = new Thread(m); 9 Class c = ThreadGroup.class; 10 Method declaredMethod = c.getDeclaredMethod("add", Thread.class); 11 Method method = declaredMethod; 12 method.setAccessible(true); 13 method.invoke(tg,t2); 14 System.out.println(Thread.activeCount()); 15 System.out.println(t1.getName()); 16 System.out.println(tg.getName()); 17 } 18 } 19 class MyRunnable implements Runnable{ 20 public void run() {} 21 }
這裏是利用反射的機制,調用ThreadGroup類中的add(Thread t)方法,把線程添加到指定的線程池,這裏要說明的是,創建的線程對象時,Thread類構造器調用的init初始化方法,並沒有把線程加入指定的線程池,而是在start方法中調用了ThreadGroup的add方法。
未完待續......
入坑兩個月,java.lang包下的Thread類