ok,什么是线程? java线程是执行程序的最小单位,jvm内存模型中,为每个java线程维护程序计数器,通过pc取字节码进行解析、操作
众所周知, 线程搞来搞去就那么几种方法:start()、stop()、interrupt()、interrupted()、isInterrrupted()、yield()、sleep()、join(), 下面就来逐个看一下它们到底是什么

Thread.State

首先的首先,先来看一下jvm线程的几种状态:

 1 public enum State {
 2         /**
 3          * 线程未启动状态
 4          */
 5         NEW,
 6 
 7         /**
 8          * 在执行状态,但不排除等待其他操作系统资源的情况
 9          */
10         RUNNABLE,
11 
12         /**
13          * 等待监视器锁的状态
14          */
15         BLOCKED,
16 
17         /**
18          * 等待状态, 会由object.wait(), join(), LockSupport.park()引起
19          */
20         WAITING,
21 
22         /**
23          * 有超时限制的等待状态,相比于WAITING,多了一个等待的时间
24          */
25         TIMED_WAITING,
26 
27         /**
28          * 结束状态
29          */
30         TERMINATED;
31 }

乍一看BLOCKED和WAITING好像差不多, 去gg了一下, 调用oject.wait()后,进入WAITING状态,会等待object.notify/notifyall,收到这个信号后,会进入BLOCKED,意思是一直阻塞知道获取监视器
这也就是notify 和 notifyall的区别

start()

 1 public synchronized void start() {
 2         if (threadStatus != 0)
 3             throw new IllegalThreadStateException();
 4 
 5         /* Notify the group that this thread is about to be started
 6          * so that it can be added to the group's list of threads
 7          * and the group's unstarted count can be decremented. */
 8         group.add(this);
 9 
10         boolean started = false;
11         try {
12             start0();
13             started = true;
14         } finally {
15             try {
16                 if (!started) {
17                     group.threadStartFailed(this);
18                 }
19             } catch (Throwable ignore) {
20                 /* do nothing. If start0 threw a Throwable then
21                   it will be passed up the call stack */
22             }
23         }
24 }
  • 首先,校验线程状态,如果已经启动过了,就会抛出异常,表明一个线程只能启动一次,就算是结束之后

  • 把线程加入一个group中, 这是一个ThreadGroup实例, 大致就是用来把线程组织成层次结构,方便管理线程, 具体没有深究

  • 调用start0()这个native方法,来由jvm启动这个线程

stop()

 1 public final synchronized void stop(Throwable obj) {
 2 	if (obj == null)
 3 		throw new NullPointerException();
 4 
 5 	SecurityManager security = System.getSecurityManager();
 6 	if (security != null) {
 7 		checkAccess();
 8     	if ((this != Thread.currentThread()) ||
 9 			(!(obj instanceof ThreadDeath))) {
10                 security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);
11             }
12         }
13    	// A zero status value corresponds to "NEW", it can't change to
14     // not-NEW because we hold the lock.
15     if (threadStatus != 0) {
16         resume(); // Wake up thread if it was suspended; no-op otherwise
17     }
18 
19     // The VM can handle all thread states
20     stop0(obj);
21 }
  • stop() 最终会调用 stop(Throwable obj)

  • 先做一些安全性的检查

  • 将线程resume(最终会调用native的resume0, 由JVM来完成)

  • 由JVM完成stop0工作

我们都知道, stop早已被deprecated了,为什么呢?
当一个线程被中止时,它所获得的对象的监视器锁都会被释放, 但由于工作没有做完,导致对象是一个中止的状态, 那么线程间的一些数据同步就会被打乱,因此它是不安全的

那怎么办嘞?

interrupt()

 1 public void interrupt() {
 2 	if (this != Thread.currentThread())
 3 		checkAccess();
 4 
 5     synchronized (blockerLock) {
 6         Interruptible b = blocker;
 7         if (b != null) {
 8             interrupt0();           // Just to set the interrupt flag
 9             b.interrupt(this);
10             return;
11         }
12     }
13     interrupt0();
14 }
  • 如果是其他线程调用interrupt,要检查安全性, 如果当前线程没有权限修改这个线程,则抛出SecurityException

  • 用一个blockerLock来保证native的interrupt0是线程安全的

  • 本质是interrupt0只是设置了中断标志位

  • 如果线程阻塞在object.wait()、join()、sleep(), 那么将会清除中断标志,并收到InterruptedException

  • 如果线程阻塞在一个InterruptibleChannel的I/O操作中, 中断位会被设置,并收到ClosedByInterruptException (属于java nio的内容, 留着以后看)

  • 如果阻塞在一个java.nio.channels.Selector, 中断位会被设置, 并且会返回一个非零值 (也属于java.nio.channels, 留看)

  • 如果上述情况都没有发生,就会设置中断标志位

  • 如果终端一个非alive的线程(如果一个线程已经启动,并且没有结束,那就是alive),啥也不会发生

基本上只是把注释翻译了一遍, 但是这样看来, interrupt跟我们理解的中断不太一样,它只是将中断标志设置了一下,除非线程在一些阻塞情况下会收到中断异常,其它状况下,什么也不会发生

interrupted() && isInterrupted()

1 public static boolean interrupted() {
2    return currentThread().isInterrupted(true);
3 }
4 
5 public boolean isInterrupted() {
6     return isInterrupted(false);
7 }

因此,我们要看的其实是

1 /**
2  * Tests if some Thread has been interrupted.  The interrupted state
3  * is reset or not based on the value of ClearInterrupted that is
4  * passed.
5  */
6 private native boolean isInterrupted(boolean ClearInterrupted);
  • 显然, interrupted返回中断标志的状态并且将中断标志清除, 而isInterrupted并不清除中断标志

yield()

 1 /**
 2      * A hint to the scheduler that the current thread is willing to yield
 3      * its current use of a processor. The scheduler is free to ignore this
 4      * hint.
 5      *
 6      * <p> Yield is a heuristic attempt to improve relative progression
 7      * between threads that would otherwise over-utilise a CPU. Its use
 8      * should be combined with detailed profiling and benchmarking to
 9      * ensure that it actually has the desired effect.
10      *
11      * <p> It is rarely appropriate to use this method. It may be useful
12      * for debugging or testing purposes, where it may help to reproduce
13      * bugs due to race conditions. It may also be useful when designing
14      * concurrency control constructs such as the ones in the
15      * {@link java.util.concurrent.locks} package.
16      */
17     public static native void yield();
  • 也是一个native方法, 大致就是做一个好人, 告诉调度器自己愿意把当前的处理器让出来, 至于说辅助并发控制, 这里留一个疑问, 以后研究lock的时候再看看

sleep()

  • 也是一个本质native方法, 源码就不放了, 要注意的是, 在sleep过程中,线程并没有放弃它所持有的对象监控器的所有权,也就是我们理解的没有释放锁, 但是在sleep期间是会释放cpu的, 具体的调度由JVM乃至OS去完成

join()

 1 /**
 2      * Waits for this thread to die.
 3      *
 4      * <p> An invocation of this method behaves in exactly the same
 5      * way as the invocation
 6      *
 7      * <blockquote>
 8      * {@linkplain #join(long) join}{@code (0)}
 9      * </blockquote>
10      *
11      * @throws  InterruptedException
12      *          if any thread has interrupted the current thread. The
13      *          <i>interrupted status</i> of the current thread is
14      *          cleared when this exception is thrown.
15      */
16     public final void join() throws InterruptedException {
17         join(0);
18     }
  • 等待这个线程结束, 如果interrupt了等待中的线程(也就是当前线程, 注意,不是被等待的那个线程), 会抛出InterruptedExecption

总结

  1. 罗列了一下线程这些常用操作的源代码,简单的翻译了一下注释, 大致搞清楚它们的作用, 由于涉及native代码, 因此也没有深究线程调度的深层机制, 这块涉及到jvm对于线程的管理,以后有机会还是要继续学习,但还不是我这个阶段的目标

  2. 所以我们知道了interrupt到底干了什么,stop为什么是不安全的,至于如何优雅的结束一个线程,我以为要不断的check中断标志位,根据具体的需求来决定是否要响应这个中断

  3. 涉及到对象监视器的概念, 现在已经知道这是用来控制对象的所有权的, 后续会慢慢深入理解它到底是个什么鬼、线程又是如何抢占它的