Thread类源码剖析 一、Thread类的简介 Java虚拟机允许应用程序同时运行多个执行线程,每个线程都有一个优先级,具有较高优先级的线程优先于具有较低优先级的线程执行。每个线程可能也可能不被标记为守护进程,这默认取决于创建该线程的父线程,优先级大小的设置也是同理。 当Java虚拟机启动时,通常有一个非守护线程
(它通常调用某个指定类的main方法,即使就是main线程
)。Java虚拟机会一直执行直到发生以下任一情况:
Runtime类的exit方法已被调用,并且SecurityManager安全管理器已允许进行退出操作。
所有非守护线程
都已死亡,原因是从对run方法的调用返回或抛出传播到run方法之外的异常。
二、创建线程的四种方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class PrimeThread extends Thread { long minPrime; PrimeThread(long minPrime) { this .minPrime = minPrime; } @Override public void run () { } } Thread thread = new PrimeThread (143 );thread.start();
1 2 3 4 5 6 7 8 9 10 11 12 13 class PrimeRun implements Runnable { long minPrime; PrimeRun(long minPrime) { this .minPrime = minPrime; } public void run () { } } PrimeRun p = new PrimeRun (143 );new Thread (p).start();
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class PrimeCall implements Callable <Long> { long minPrime; PrimeCall(long minPrime) { this .minPrime = minPrime; } @Override public Long call () throws Exception { return 149L ; } } FutureTask<Long> futureTask = new FutureTask <>(new PrimeCall (143 )); Thread thread = new Thread (futureTask);thread.start();
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class PrimeRun implements Runnable { long minPrime; PrimeRun(long minPrime) { this .minPrime = minPrime; } @Override public void run () { } } PrimeRun p = new PrimeRun (143 );ExecutorService threadPool = Executors.newFixedThreadPool(100 );threadPool.execute(p);
三、Thread类的重要属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 private volatile String name;private int priority;private boolean daemon = false ;private boolean stillborn = false ;private long eetop;private Runnable target;private ThreadGroup group;private ClassLoader contextClassLoader;private AccessControlContext inheritedAccessControlContext;private static int threadInitNumber;private static synchronized int nextThreadNum () { return threadInitNumber++; } ThreadLocal.ThreadLocalMap threadLocals = null ; ThreadLocal.ThreadLocalMap inheritableThreadLocals = null ; private final long stackSize;private final long tid;private static long threadSeqNumber;private static synchronized long nextThreadID () { return ++threadSeqNumber; } private volatile int threadStatus;private volatile Interruptible blocker;private final Object blockerLock = new Object (); public static final int MIN_PRIORITY = 1 ;public static final int NORM_PRIORITY = 5 ;public static final int MAX_PRIORITY = 10 ;
我们上面谈到了Thread类的一些重要属性,基本涵括了主要的属性,这些属性也是我们后面构造方法和其他重要方法操作的对象,遇到时我们可能还会详细展开说一下。
四、Thread类的构造方法
注意Thread有很多重载的构造方法,不过其实都是调用图中标注的参数最全的构造方法(前面有个红色的锁标记,表示该构造方法是私有的,不可调用,只作为其他构造方法内部使用),下面我们单独看看这个构造方法里面做了那些事情:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 private Thread (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 ) { if (security != null ) { g = security.getThreadGroup(); } if (g == null ) { g = parent.getThreadGroup(); } } g.checkAccess(); if (security != null ) { if (isCCLOverridden(getClass())) { security.checkPermission( SecurityConstants.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); this .stackSize = stackSize; this .tid = nextThreadID(); }
可以看到,Thread类的构造也没有我们想象的那么复杂,唯一比较难理解的是安全管理器的方面,不过默认我们的应用程序并不会开启安全管理器,除非我们通过==System.setSecurityManager(new SecurityManager());==,或者通过启动参数==-Djava.security.manager==,注意安全管理器配置文件的内容类似于下面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 grant { permission java.net.SocketPermission "localhost:0" , "listen" ; permission java.util.PropertyPermission "java.version" , "read" ; permission java.util.PropertyPermission "java.vendor" , "read" ; permission java.util.PropertyPermission "java.vendor.url" , "read" ; permission java.util.PropertyPermission "java.class.version" , "read" ; permission java.util.PropertyPermission "os.name" , "read" ; permission java.util.PropertyPermission "os.version" , "read" ; permission java.util.PropertyPermission "os.arch" , "read" ; permission java.util.PropertyPermission "file.separator" , "read" ; permission java.util.PropertyPermission "path.separator" , "read" ; permission java.util.PropertyPermission "line.separator" , "read" ; permission java.util.PropertyPermission "java.specification.version" , "read" ; permission java.util.PropertyPermission "java.specification.vendor" , "read" ; permission java.util.PropertyPermission "java.specification.name" , "read" ; permission java.util.PropertyPermission "java.vm.specification.version" , "read" ; permission java.util.PropertyPermission "java.vm.specification.vendor" , "read" ; permission java.util.PropertyPermission "java.vm.specification.name" , "read" ; permission java.util.PropertyPermission "java.vm.version" , "read" ; permission java.util.PropertyPermission "java.vm.vendor" , "read" ; permission java.util.PropertyPermission "java.vm.name" , "read" ; };
我们可以通过==-Djava.security.manager -Djava.security.policy=”/usr/local/java.policy”==同时指定配置文件的路径。
五、Thread类的六种状态 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public enum State { NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED; }
六、Thread类的重要方法 第一个出场的就是编写多线程程序基本必备的start
方法,JVM虚拟机会启动新线程并调用线程的run
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 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) { } } } private native void start0 () ;
注意Thread类也继承了Runnable方法并重写run方法,Thread的run方法的逻辑是:如果创建线程时传入了Runnable对象则调用Runnable对象的run方法,否则就是继承Thread类并重写该run方法。
1 2 3 4 5 6 @Override public void run () { if (target != null ) { target.run(); } }
我们来看看第二个出场的方法,没错,它就是我们经常使用的Thread.sleep
方法,使当前正在执行的线程休眠(暂时停止执行)指定的毫秒数,具体取决于系统计时器和调度程序的精度和准确性。线程不会失去任何监视器/锁的所有权!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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;
第三个出场的方法就是在线程同步尤其是主线程等待多个子线程返回的场景下经常使用的join
方法,会阻塞等待线程执行结束返回,底层其实就是调用线程的wait方法等待。
注意,JVM会保证线程结束时调用线程的notifyAll方法确保等待的join方法成功返回。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 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; } } }
第四个出场的是我们的中断系列方法,对于wait系列、join系列、sleep系列的方法处于阻塞等待状态
时线程被中断会抛出InterruptedException
异常,并且清除线程中断状态
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public void interrupt () { if (this != Thread.currentThread()) { checkAccess(); synchronized (blockerLock) { Interruptible b = blocker; if (b != null ) { interrupt0(); b.interrupt(this ); return ; } } } interrupt0(); }
与上面的interrupt
方法搭配使用的常常还有下面的检查线程当前中断状态的一些方法:
1 2 3 4 5 6 7 8 9 10 11 12 public static boolean interrupted () { return currentThread().isInterrupted(true ); } public boolean isInterrupted () { return isInterrupted(false ); } @HotSpotIntrinsicCandidate private native boolean isInterrupted (boolean ClearInterrupted) ;
七、Thread类的异常处理机制 我们有必要插一嘴,在JSR-166标准引入了UncaughtExceptionHandler
函数式接口,它是当线程由于未捕获的异常而突然终止时调用的处理程序的接口。
当线程由于未捕获的异常而即将终止时,Java虚拟机将使用getUncaughtExceptionHandler
查询线程的UncaughtExceptionHandler,并调用处理程序的uncaughtException方法,将线程和异常作为参数传递。如果线程尚未显式设置其UncaughtExceptionHandler,则其ThreadGroup对象将充当其UncaughtExceptionHandler。如果ThreadGroup对象对处理异常没有特殊要求,它可以将调用转发到默认的未捕获异常处理程序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 private volatile UncaughtExceptionHandler uncaughtExceptionHandler;private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;public UncaughtExceptionHandler getUncaughtExceptionHandler () { return uncaughtExceptionHandler != null ? uncaughtExceptionHandler : group; } @FunctionalInterface public interface UncaughtExceptionHandler { void uncaughtException (Thread t, Throwable e) ; }
如果没有指定线程的未捕获异常处理程序,则默认使用其线程组对象充当异常处理程序,因为ThreadGroup
类也继承了UncaughtExceptionHandler
接口(下面代码展示):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public void uncaughtException (Thread t, Throwable e) { if (parent != null ) { parent.uncaughtException(t, e); } else { Thread.UncaughtExceptionHandler ueh = Thread.getDefaultUncaughtExceptionHandler(); if (ueh != null ) { ueh.uncaughtException(t, e); } else if (!(e instanceof ThreadDeath)) { System.err.print("Exception in thread \"" + t.getName() + "\" " ); e.printStackTrace(System.err); } } }
正常我们不做任何编码限制的情况下,创建的线程默认归属父线程即main线程的线程组,如果线程执行中抛出了未捕获的异常,JVM会调用线程组的uncaughtException
方法,根据上面ThreadGroup的uncaughtException方法实现我们可以看出,会找到最终的根线程组并调用其defaultUncaughtExceptionHandler的uncaughtException方法,打印异常栈的追踪轨迹信息。
下面我们简单的实践一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 public class ThreadTest { public static void main (String[] args) { System.out.println("Thread.currentThread().getThreadGroup() = " + Thread.currentThread().getThreadGroup()); System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName()); System.out.println("Thread.currentThread().getPriority() = " + Thread.currentThread().getPriority()); System.out.println("Thread.currentThread().getState() = " + Thread.currentThread().getState()); System.out.println("Thread.currentThread().getContextClassLoader() = " + Thread.currentThread().getContextClassLoader()); System.out.println("Thread.currentThread().getId() = " + Thread.currentThread().getId()); System.out.println("Thread.currentThread().getUncaughtExceptionHandler() = " + Thread.currentThread().getUncaughtExceptionHandler()); int i = 1 / 0 ; System.out.println("---" ); } }
从上图也能看出main线程的线程组信息,并且其未捕获异常处理程序默认也就是本身的线程组对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class ThreadTest { public static void main (String[] args) { Thread thread = new Thread (() -> { System.out.println("I am ThreadA" ); int i = 1 / 0 ; System.out.println("I am King" ); }, "ThreadA" ); System.out.println("thread.getThreadGroup() = " + thread.getThreadGroup()); System.out.println("thread.getName() = " + thread.getName()); System.out.println("thread.getContextClassLoader() = " + thread.getContextClassLoader()); System.out.println("thread.getUncaughtExceptionHandler() = " + thread.getUncaughtExceptionHandler()); System.out.println("thread.getState() = " + thread.getState()); thread.start(); } }
从上图可以看出创建的新线程的线程组就是其父线程(main线程)的线程组,所用的未捕获异常处理程序默认也就是本身的线程组对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class ThreadTest { public static void main (String[] args) { Thread thread = new Thread (() -> { System.out.println("I am ThreadA" ); int i = 1 / 0 ; System.out.println("I am King" ); }, "ThreadA" ); thread.setUncaughtExceptionHandler((t,e) -> { System.out.println(t.getName() + " : " + e.getMessage()); }); System.out.println("thread.getThreadGroup() = " + thread.getThreadGroup()); System.out.println("thread.getName() = " + thread.getName()); System.out.println("thread.getContextClassLoader() = " + thread.getContextClassLoader()); System.out.println("thread.getUncaughtExceptionHandler() = " + thread.getUncaughtExceptionHandler()); System.out.println("thread.getState() = " + thread.getState()); thread.start(); } }
从上图能看出,当我们给创建的线程设置未捕获异常处理程序后,一旦线程因为未捕获异常而终止时,JVM会使用我们设置的未捕获异常处理程序处理该异常!