大数跨境

Java项目性能瓶颈分析及定位(八)——Java线程堆栈分析(五)

Java项目性能瓶颈分析及定位(八)——Java线程堆栈分析(五) 慧测
2017-08-21
2
导读:Java项目性能瓶颈分析及定位系列文章——解读锁


课程大纲及开课时间

www.huicewang.com



对于CPU而言,常见的瓶颈主要有两种:

  1. 服务器的压力很小,但是CPU的利用率却很高,这样的性能瓶颈相对比较容易定位(好比我只是说了你一句,你就哭了,你的弱点立马就暴露出来了);


  2. 给服务器施加的压力很大,但是CPU的利用率总是很低,也就是压力上不去,这类瓶颈最常见,定位起来也最困难(类似我对你用尽了各种手段,你就是不说实话,内心太强大了)。


影响压力上不去的因素很多,但是对于性能测试工程师而言,最具价值的瓶颈肯定是由于代码问题引起的了(定位到是代码问题导致的压力上不去,特有成就感),而导致这类问题出现的最常见的一个知识点,肯定就是了,如果对于Java多线程的锁机制不甚了解,分析定位代码的瓶颈和性能调优几乎是不可能的。


锁——确保多线程访问的数据一致性。


目前Java多线程的锁基本就是通过如下两种方式实现:

  1. synchronized关键字

  2. java.util.concurrent.locks包下的类(用的也蛮多,稍微有点复杂,课堂上讲吧)

今天就为大家分享synchronized关键字实现的加锁方式。


认识synchronized

假设这样一个场景,张三和李四都需要维修各自的电脑,但是只有一把螺丝刀工具,意味着同一时刻只能有一个人使用这把工具修他们的电脑,那么使用Java代码该如何实现呢?我们把张三和李四比喻成两个线程,把他们修电脑的过程用Java语言实现一下,开始!


上面代码执行后对线程做dump,摘取部分信息如下:


解读锁

wait()和sleep()

它们有一个共同点是,都会把当前的线程阻塞住( 时长为函数参数指定的时间), 我们称之为睡眠或者等待。但二者实际上是完全不同的两个函数, 二者有着最为本质的区别:


wait()  当线程执行到wait()方法上, 当前线程会释放监视锁, 此时其它线程可以占有该锁,一旦wait()方法执行完成, 当前线程又继续尝试获取该锁, 直到执行完该锁的作用域。 可以说wait()是多线程场合下用得最多的一个方法。 结合notify(),可以实现两个线程之间的通信,一个线程可以通过这种方法通知另一个线程继续执行, 完成线程之间的配合。 wait()和锁的示意图如下:


在wait(5000)这5秒( 5000毫秒) 期间, 当前线程会释放它占有的锁, 此时其它线程有机会获得该锁。 当wait(5000)执行完成后, 当前线程继续获得该锁的使用权。 满足如下条件之一, wait()方法退出:

  • 达到了等待的时间之后, 自动退出。 如wait(5000),5秒后wait方法退出。

  • 其它的线程调用了该锁的notify()方法。 如果多个线程在等待同一个锁, 只有一个线程会被通知到。


sleep() 与锁操作无关, 如果该方法恰好在一个锁的保护范围之内, 当前线程即使在执行sleep()的时候, 仍然继续保持监视锁。 该方法实际上仅仅是完成等待或者睡眠的语义。 示意图如下:


从上面的代码Thread.sleep(5000)可以看出, sleep()方法并不是锁上面的一个方法, 而是线程的一个静态方法。 也就是说该方法实际上是和锁操作无关的。 如果sleep()方法恰好在一个锁的保护范围之内, 那么当前线程即使执行到该sleep方法, 也不会产生特别的锁操作(持有锁或者释放锁), 如果原来持有, 现在仍然持有。 如果原来没有持有, 那么现在仍然不持有。


还记得第六篇文章解读的线程堆栈吗?包含的直接信息有:

线程的个数、 每个线程调用的方法堆栈、 当前锁的状态。 线程的个数可以直接数出来; 线程调用的方法堆栈, 从下向上看,即表示当前的线程调用了哪个类上的哪个方法。 而锁的状态看起来稍微有一点技巧。 与锁相关的三个重要信息如下:


  • 当一个线程占有一个锁的时候, 线程堆栈中会打印—locked <0x24a23208>

  • 当一个线程正在等待其它线程释放该锁, 线程堆栈中会打印—waiting to lock <0x24a23208>

  • 当一个线程占有一个锁, 但又执行到该锁的wait()上, 线程堆栈中首先打印locked,然后又会打印—waiting on <0x24a23208>


源码:



运行上面的程序,打印堆栈信息如下:


从上面这个例子中, 可以很清晰地看出, 在线程堆栈中与锁相关的三个最重要的特征字:lockedwaiting to lockwaiting on;了解这三个特征字, 就能够对锁进行分析了。


一般情况下, 当一个(些)线程在等待一个锁时, 应该有一个线程占用这个锁, 即如果有的线程在等待一个锁, 该锁必然被另一个线程占有了, 也就是说, 从打印的堆栈中如果能看到waiting to lock <0x04d02238>,应该也应该能找到一个线程locked <0x04d02238>。


下篇文章开始,我们来解读线程的状态,这也是相当重要的啦!


本着学习交流之目的,如有错误或不当之处,欢迎批评指正,共同进步!

扫描二维码,报名慧测免费公开课








2017年8月16日起,慧测免费公开课系列正式开启——

连续两个月的Java语言+Selenium实战尽在慧测腾讯课堂!


  • 软件测试工程师专属的Java语言学习盛宴!

  • 紧密围绕性能测试、自动化测试、测试开发所必备的Java编程知识讲解!

  • 为后续一个月免费的Selenium实战课程学习做准备!


本系列公开课报名地址:

https://ke.qq.com/course/229575#tuin=3e1d63a


或者扫描下方二维码直接报名


感谢您关注慧测:

慧测官网:www.huicewang.com
慧测公开课服务群: 623636110

课程咨询微信/QQ:18518511087/2657535456

公众号:慧测


【声明】内容源于网络
0
0
慧测
专注人工智能前沿技术落地企业实战应用
内容 404
粉丝 0
慧测 专注人工智能前沿技术落地企业实战应用
总阅读104
粉丝0
内容404