java线程简述
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
总结
-
罗列了一下线程这些常用操作的源代码,简单的翻译了一下注释, 大致搞清楚它们的作用, 由于涉及native代码, 因此也没有深究线程调度的深层机制, 这块涉及到jvm对于线程的管理,以后有机会还是要继续学习,但还不是我这个阶段的目标
-
所以我们知道了interrupt到底干了什么,stop为什么是不安全的,至于如何优雅的结束一个线程,我以为要不断的check中断标志位,根据具体的需求来决定是否要响应这个中断
-
涉及到对象监视器的概念, 现在已经知道这是用来控制对象的所有权的, 后续会慢慢深入理解它到底是个什么鬼、线程又是如何抢占它的