Java Thread 那些事(一)

2014-11-24 07:53:32 · 作者: · 浏览: 0
这篇文章被压在草稿箱许久,最近公司内部的技术社区有同学贴出了几篇分享Java线程的文章,发觉有很多概念没有讲清楚,所以花点时间继续撰写,便有了这篇博文。本文只聚焦JVM层面的线程模型,不考虑和真实的操作系统Thread模型挂钩(由于篇幅有限,本文不会介绍Thread dump结构,也不会介绍调优过程中对工具的综合使用,如ps,top,iostat,jstack,TDA plugin,Thread inspector.如果有问题,欢迎大家留言交流)。后面会考虑对xUnix和Windows平台的线程/进程模型进行深入分析,也希望大家能够喜欢。

ok,言归正传。上图:

\

Java的线程状态一共有NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED 6种状态。这里重点关注一下BLOCKED和TIMED_WAITING状态。

BLOCKED状态:线程进入此状态的前提一般有两个:waiting for monitZ http://www.2cto.com/kf/ware/vc/" target="_blank" class="keylink">vcqOoaW50cmluc2ljIG9yIGV4dGVybmFso6kgZW50cnkgu/LV3yByZWVudGVyIM2ssr20+sLrv+mho72ytb3V4s7Sw8fPyMHLveLSu8/CSmF2Yc/fs8zEo9DN1tC1xMG9uPa208HQoaPI5828y/nKvqO6PC9wPgo8cD48YnI+CjwvcD4KPHA+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxpbWcgc3JjPQ=="https://www.cppentry.com/upload_files/article/76/1_poxxj__.png" alt="\">


每个 Monitor在某个时刻,只能被一个线程拥有,该线程就是 “Active Thread”,而其它线程都是 “Waiting Thread”,分别在两个队列 “Entry Set”和 “Wait Set”里面等候。在 “Entry Set”中等待的线程状态是 “Waiting for monitor entry”,而在 “Wait Set”中等待的线程状态是 “in Object.wait()”。如果你不恰当的使用了ReentrantLock或者ReentrantReadWriteLock类,就有可能陷入BLOCKED状态,这个也是我们调优中经常会遇到的情况,解决方案也很简单,找到等待上锁的地址,分析是否发生了Thread starvation。
至于TIME_WAITING状态,官方文档也讲解的比较好,即你在调用下面方法时,线程会进入该状态。

          Thread.sleep 
	  Object.wait with timeout 
	  Thread.join with timeout 
	  LockSupport.parkNanos 
	  LockSupport.parkUntil

这里重点关注一下LockSupport,该类是用来创建锁和其他同步类的基本线程阻塞原语,是一个针对Thread.suspend和Thread.resume()的优化,也是针对忙等,防止过度自旋的一种优化(关于这一点,感兴趣的同学可以参阅一下文献5)。


ok,在简单介绍完几个重点的线程状态后,我们通过几个具体的case来看了解下Thread stack:

Case 1:NIO 中的Acceptor

"qtp589745448-36 Acceptor0 SelectChannelConnector@0.0.0.0:8161" prio=10 tid=0x00007f02f8eea800 nid=0x18ee runnable [0x00007f02e70b3000]
   java.lang.Thread.State: RUNNABLE
	at sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method)
	at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:241)
	- locked <0x00000000ec8ffde8> (a java.lang.Object)
	at org.eclipse.jetty.server.nio.SelectChannelConnector.accept(SelectChannelConnector.java:109)
	at org.eclipse.jetty.server.AbstractConnector$Acceptor.run(AbstractConnector.java:938)
	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608)
	at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:543)
	at java.lang.Thread.run(Thread.java:724)

   Locked ownable synchronizers:
	- None

瞅瞅源代码中是怎么实现的,如下:

public void accept(int acceptorID) throws IOException
100    {
101        ServerSocketChannel server;
102        synchronized(this)
103        {
104            server = _acceptChannel;
105        }
106
107        if (server!=null && server.isOpen() && _manager.isStarted())
108        {
109            SocketChannel channel = server.accept();
110            channel.configureBlocking(false);
111            Socket socket = channel.socket();
112            configure(socket);
113            _manager.register(channel);
114        }
115    }

关于Thread stack,这里强调一点:nid,native lwp id,即本地轻量级进程(即线程)ID。

Case 2: NIO从的Selector

"qtp589745448-35 Selector0" prio=10 tid=0x00007f02f8ee9800 nid=0x18ed runnable [0x00007f02e71b4000]
   java.lang.Thread.State: RUNNABLE
	at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
	at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:228)
	at sun.nio.ch.EPollSelectorImpl.doSelect(EPoll