一文看盡Java-Thread
一、前言
主要分成兩部說起:Thread原始碼解讀和常見面試題解答,廢話不多說開始;
二、原始碼解讀
首先看下建構函式,建構函式都是通過呼叫init方法對屬性進行初始化,主要是對執行緒組、執行緒名字、棧大小等資訊進行初始化;init內部通過呼叫currentThread本地方法,獲取當前的執行緒,這個本地方法封裝在JVM中,有興趣的可以看下這個這個連結查詢下JVM實現https://hg.openjdk.java.net/jdk8u,接下來對ThreadGroup的判斷,如果沒有傳入執行緒組的話, 第一是使用SecurityManager中的ThreadGroup, 如果從SecurityManager 中獲取不到ThreadGroup(), 那麼就從當前執行緒中獲取執行緒組,最後做了檢驗和些引數的賦值,整體上相對比較簡單;
private void init(ThreadGroup g, Runnable target, String name, long stackSize) { init(g, target, name, stackSize, null); } 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 */ tid = nextThreadID(); } public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); } public Thread(Runnable target) { init(null, target, "Thread-" + nextThreadNum(), 0); } Thread(Runnable target, AccessControlContext acc) { init(null, target, "Thread-" + nextThreadNum(), 0, acc); } // 執行緒名 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, long stackSize) { init(group, target, name, stackSize); }
接下來看下主要的屬性:
// 類載入的時候,呼叫本地的註冊本地方靜態方法, 這個方法是本地方法 private static native void registerNatives(); static { registerNatives(); } private volatile char 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. */ // 要執行的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. */ // 給這個執行緒設定的棧的大小,預設為0 private long stackSize; /* * JVM-private state that persists after native thread termination. */ private long nativeParkEventPointer; /* * Thread ID */ //執行緒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; private static synchronized long nextThreadID() { return ++threadSeqNumber; } /** * 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; private final Object blockerLock = new Object(); /* Set the blocker field; invoked via sun.misc.SharedSecrets from java.nio code */ void blockedOn(Interruptible b) { synchronized (blockerLock) { blocker = b; } } /** * The minimum priority that a thread can have. */ // 執行緒執行的最低優先順序 為1 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;
最後介紹下方法的作用和執行緒狀態,原始碼都比較簡單,沒必進行過多的介紹,都是通過呼叫JVM的本地方法實現;
執行緒狀態:
三、常見面試題
1.執行緒與程序的區別?
程序是資源分配最小的單位,執行緒是CPU排程最小的單位;
執行緒屬於程序,共享程序分配的資源;
程序屬於搶佔式排程,資源不相互共享;
2.start和run的區別?
run是Thread的一個普通的方法;
start方法會建立一個新的子執行緒並啟動;
3.sleep與wait的區別?
sleep是Thread方法,wait是Object的方法;
wait方法只能在synchroized方法或者塊中使用;
Thread.sleep只會讓出CPU,不會改變鎖的行為;
Object.wait不僅會讓出CPU,同時還會釋放佔有同步資源的鎖;
4.執行緒狀態的轉化?
圖中將WAITING 和TIMED_WAITING 兩個狀態合併為WAITING ,沒有分開,大家不要搞錯;
5.如何處理執行緒的返回值?
主執行緒等待法,使用while等待主執行緒返回值;
join阻塞當前執行緒以等待子執行緒;
通過FuTureTask獲取子執行緒的返回值;
public class MyCallable implements Callable<String> { @Override public String call() throws Exception { String value="test"; System.out.println("start"); Thread.sleep(5000); System.out.println("end"); return value; } } public class FutureTaskDemo { public static void main(String[] main) throws ExecutionException, InterruptedException { FutureTask<String> futureTask=new FutureTask<String>(new MyCallable()); new Thread(futureTask).start(); if (!futureTask.isDone()){ System.out.println("waiting"); } System.out.println("return"+futureTask.get()); } }View Code
通過執行緒池獲取返回值;
public class ThreadPoolDemo { public static void main(String[] args){ ExecutorService executorService= Executors.newCachedThreadPool(); Future<String> futureTask=executorService.submit(new MyCallable()); if (!futureTask.isDone()){ System.out.println("wait"); } try { System.out.println(futureTask.get()); }catch (InterruptedException ex){ ex.printStackTrace(); }catch (ExecutionException ex){ ex.printStackTrace(); }finally { executorService.shutdown(); } } }View Code
6.Thread和Runnable?
Thread是類,Runnable是介面,Thread是Runnable實現;
類的繼承單一原則,Runnable是更高層次的抽象;
四、結束
歡迎大家加群438836709!歡迎大家關注我!
&n