Java Thread 那些事
这篇文章被压在草稿箱许久,最近公司内部的技术社区有同学贴出了几篇分享 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 monitor(intrinsic or external) entry 或者 reenter 同步代码块。讲到这我们先了解一下Java线程模型中的两个队列。如图所示:
每个 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(EPollSelectorImpl.java:81)
at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:87)
- locked <0x00000000ec9006f0> (a sun.nio.ch.Util$2)
- locked <0x00000000ec9006e0> (a java.util.Collections$UnmodifiableSet)
- locked <0x00000000ec9004c0> (a sun.nio.ch.EPollSelectorImpl)
at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:98)
at org.eclipse.jetty.io.nio.SelectorManager$SelectSet.doSelect(SelectorManager.java:569)
at org.eclipse.jetty.io.nio.SelectorManager$1.run(SelectorManager.java:290)
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
代码片段如下:
// If we should wait with a select
566 if (wait>0)
567 {
568 long before=now;
569 selector.select(wait);
570 now = System.currentTimeMillis();
571 _timeout.setNow(now);
572
573 // If we are monitoring for busy selector
574 // and this select did not wait more than 1ms
575 if (__MONITOR_PERIOD>0 && now-before <=1)
576 {
577 // count this as a busy select and if there have been too many this monitor cycle
578 if (++_busySelects>__MAX_SELECTS)
579 {
580 // Start injecting pauses
581 _pausing=true;
582
583 // if this is the first pause
584 if (!_paused)
585 {
586 // Log and dump some status
587 _paused=true;
588 LOG.warn("Selector {} is too busy, pausing!",this);
589 }
590 }
591 }
592 }
Case 3: ActveMQ中针对MQTT协议的Handler
"ActiveMQ Transport Server Thread Handler: mqtt://0.0.0.0:1883?maximumConnections=1000&wireFormat.maxFrameSize=104857600" daemon prio=10 tid=0x00007f02f8ba6000 nid=0x18dc waiting on condition [0x00007f02ec824000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000faad0458> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:226)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2082)
at java.util.concurrent.LinkedBlockingQueue.poll(LinkedBlockingQueue.java:467)
at org.apache.activemq.transport.tcp.TcpTransportServer$1.run(TcpTransportServer.java:373)
at java.lang.Thread.run(Thread.java:724) Locked ownable synchronizers:
- None
代码片段:
@Override
protected void doStart() throws Exception {
if (useQueueForAccept) {
Runnable run = new Runnable() {
@Override
public void run() {
try {
while (!isStopped() && !isStopping()) {
Socket sock = socketQueue.poll(1, TimeUnit.SECONDS);
if (sock != null) {
handleSocket(sock);
}
} } catch (InterruptedException e) {
LOG.info("socketQueue interuppted - stopping");
if (!isStopping()) {
onAcceptError(e);
}
}
}
};
socketHandlerThread = new Thread(null, run, "ActiveMQ Transport Server Thread Handler: " + toString(), getStackSize());
socketHandlerThread.setDaemon(true);
socketHandlerThread.setPriority(ThreadPriorities.BROKER_MANAGEMENT - 1);
socketHandlerThread.start();
}
super.doStart();
}
Case 5: 模拟银行转帐存款
"withdraw" prio=10 tid=0x00007f3428110800 nid=0x2b6b waiting for monitor entry [0x00007f34155bb000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.von.thread.research.DeadThread.depositMoney(DeadThread.java:13)
- waiting to lock <0x00000000d7fae540> (a java.lang.Object)
- locked <0x00000000d7fae530> (a java.lang.Object)
at com.von.thread.research.DeadThread.run(DeadThread.java:28)
at java.lang.Thread.run(Thread.java:724) Locked ownable synchronizers:
- None "deposit" prio=10 tid=0x00007f342810f000 nid=0x2b6a waiting for monitor entry [0x00007f34156bc000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.von.thread.research.DeadThread.withdrawMoney(DeadThread.java:21)
- waiting to lock <0x00000000d7fae530> (a java.lang.Object)
- locked <0x00000000d7fae540> (a java.lang.Object)
at com.von.thread.research.DeadThread.run(DeadThread.java:29)
at java.lang.Thread.run(Thread.java:724) Locked ownable synchronizers:
- None
Found one Java-level deadlock:
=============================
"withdraw":
waiting to lock monitor 0x00007f3400003620 (object 0x00000000d7fae540, a java.lang.Object),
which is held by "deposit"
"deposit":
waiting to lock monitor 0x00007f3400004b20 (object 0x00000000d7fae530, a java.lang.Object),
which is held by "withdraw" Java stack information for the threads listed above:
===================================================
"withdraw":
at com.von.thread.research.DeadThread.depositMoney(DeadThread.java:13)
- waiting to lock <0x00000000d7fae540> (a java.lang.Object)
- locked <0x00000000d7fae530> (a java.lang.Object)
at com.von.thread.research.DeadThread.run(DeadThread.java:28)
at java.lang.Thread.run(Thread.java:724)
"deposit":
at com.von.thread.research.DeadThread.withdrawMoney(DeadThread.java:21)
- waiting to lock <0x00000000d7fae530> (a java.lang.Object)
- locked <0x00000000d7fae540> (a java.lang.Object)
at com.von.thread.research.DeadThread.run(DeadThread.java:29)
at java.lang.Thread.run(Thread.java:724) Found 1 deadlock.
这里是一个非顺序加锁诱发的一个死锁场景。
好了,差不多了。 总结一下,在调优过程中,重点关注以下三类情况:
1. waiting for monitor entry – thread state blocked。可能发生的问题: deadlock(sequential deadlock,starvation deadlock...)
2. waiting on condition – sleeping or timed_waiting。可能发生的问题: IO bottleneck
3. Object.wait – TIMED_WAITING。问题:wait & notifyAll使用上需要明确其性能及其局限性问题。External锁,更多的考虑使用await和singal方法。
参考文献:
http://architects.dzone.com/articles/how-analyze-java-thread-dumps
http://stackoverflow.com/questions/7698861/simple-java-example-runs-with-14-threads-why
http://www.longene.org/forum/viewtopic.php?f=5&t=94&p=399#p399
http://www.slideshare.net/Byungwook/analysis-bottleneck-in-j2ee-application
http://docs.oracle.com/javase/1.5.0/docs/guide/misc/threadPrimitiveDeprecation.html
JavaConcurrency in practice
Java Thread 那些事的更多相关文章
- Java的哪些事
Java的哪些事--------------------------------------------------Java学习分2个方面: Java语法与Java类库 Java: A simple, ...
- Java Thread系列(七)死锁
Java Thread系列(七)死锁 当线程需要同时持有多个锁时,有可能产生死锁.考虑如下情形: 线程 A 当前持有互斥所锁 lock1,线程 B 当前持有互斥锁 lock2.接下来,当线程 A 仍然 ...
- Java Thread 的 sleep() 和 wait() 的区别
Java Thread 的使用 Java Thread 的 run() 与 start() 的区别 Java Thread 的 sleep() 和 wait() 的区别 1. sleep ...
- Java Thread 的 run() 与 start() 的区别
Java Thread 的使用 Java Thread 的 run() 与 start() 的区别 Java Thread 的 sleep() 和 wait() 的区别 1. ...
- Java Thread wait, notify and notifyAll Example
Java Thread wait, notify and notifyAll Example Java线程中的使用的wait,notify和nitifyAll方法示例. The Object clas ...
- java: Thread 和 runnable线程类
java: Thread 和 runnable线程类 Java有2种实现线程的方法:Thread类,Runnable接口.(其实Thread本身就是Runnable的子类) Thread类,默认有ru ...
- Java Thread join() 的用法
Java Thread中, join() 方法主要是让调用改方法的thread完成run方法里面的东西后, 在执行join()方法后面的代码.示例: class ThreadTesterA imple ...
- Java thread jargon
In Java thread topic, the task to be executed and the thread to drive the task are two concepts shou ...
- 性能分析之-- JAVA Thread Dump 分析综述
性能分析之-- JAVA Thread Dump 分析综述 一.Thread Dump介绍 1.1什么是Thread Dump? Thread Dump是非常有用的诊断Java应用问题的工 ...
随机推荐
- 2013杭州网络赛C题HDU 4640(模拟)
The Donkey of Gui Zhou Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/O ...
- FMDB 的基本操作
在FMDB下载文件后,工程中必须导入如下文件,并使用 libsqlite3.dylib 依赖包 FMDB同时兼容ARC和非ARC工程,会自动根据工程配置来调整相关的内存管理代码. FMDB常用类: F ...
- Android自定义控件实战——水流波动效果的实现WaveView
转载请声明出处http://blog.csdn.net/zhongkejingwang/article/details/38556891 水流波动的波形都是三角波,曲线是正余弦曲线,但是Android ...
- 竖向折叠二级导航JS代码(可防刷新ul/li结构)
<html xmlns="http://www.w3.org/1999/xhtml"><head runat="server"> ...
- c++特殊函数
C++中NULL不能写作小写,NULL的值为零,也可以写作0 在自己写的复制构造函数中不改变原对象,所以传进来的参数可以设为const类型的,这样可以保证传进来的对象不被改变 比如A(const A ...
- 触发器应用 trigger
首先有一张表: create table T_SALARY ( name VARCHAR2(20), age NUMBER(2), salary NUMBER(5) ); insert into t_ ...
- 【行业干货】2013中国零售商排名 - 课程公告板 - 京东内部论坛 - Powered by Discuz!
[行业干货]2013中国零售商排名 - 课程公告板 - 京东内部论坛 - Powered by Discuz! [行业干货]2013中国零售商排名 [复制链接] bjpanzhoulan ...
- POJ 3261 Milk Patterns(后缀数组+二分答案+离散化)
题意:给定一个字符串,求至少出现k 次的最长重复子串,这k 个子串可以重叠. 分析:经典的后缀数组求解题:先二分答案,然后将后缀分成若干组.这里要判断的是有没有一个组的符合要求的后缀个数(height ...
- Mysql 启动失败 报错 1067
Mysql装好后,重启电脑第二次发现服务无法启动.提示如下: ------------------------ MySQL 服务无法启动. 系统出错. 发生系统错误 1067. 进程意外终止. --- ...
- SQL Server 基础 04 函数与分组查询数据
函数与分组查询数据 系统函数分 聚合函数.数据类型转换函数.日期函数.数学函数 . . . 1. 聚合函数 主要是对一组值进行计算,然后返回一个值. 聚合函数包括 sum(求和).avg(求平均值). ...