并发编程的目的是为了程序运行的更快。
1.上下文切换:
时间片:cpu分配给各个线程的时间。
单核处理器也支持多线程执行代码。
cpu通过时间片分配算法来循环执行任务,任务从保存到再加载的过程就是一次上下文切换。
多线程不一定快,因为线程有创建和上下文切换的开销。
使用Lmbench可以测试上下文切换的时长(什么时候需要看这个参数呢)。
使用vmstat可以测量上下文切换的次数。比如:执行vmstat 1,再看cs的值,cs表示上下文切换的次数,这个值表示每一秒钟上下文切换的次数。(什么时候需要用到这个命令呢?)
减少上下文切换的方法:
1.无锁并发编程。多线程竞争锁时,会引发上下文切换,所以多线程处理数据时,可以用一些办法来避免使用锁,比如将数据的ID按照Hash算法取模分段,不同的线程处理不同段的数据。
2.CAS算法。eg:java的atomic包使用CAS算法来更新数据,而不需要加锁。
3.使用最少线程。
4.使用协程。协程:在单线程里实现多线程的调度,并在单线程里维持多个线程间的切换。
减少上下文切换实战:
通过减少线上大量WAITING的线程,来减少上下文切换的次数。
第一步:用jstack命令dump线程信息,看pid为3117的进程在做什么
sudo -u admin jstack 3117>/temp
第2步:统计所有线程分别处于什么状态
grep java.lang.Thread.State temp | awk '{print $2$3$4$5}' | sort | uniq -c
本来应该截图的,不太方便直接把命令和结果赋值粘贴过来:
[tengfei.fangtf@ifeve ~]$ grep java.lang.Thread.State dump17 | awk '{print $2$3$4$5}'
| sort | uniq -c
39 RUNNABLE
21 TIMED_WAITING(onobjectmonitor)
6 TIMED_WAITING(parking)
51 TIMED_WAITING(sleeping)
305 WAITING(onobjectmonitor)
3 WAITING(parking)
第三步:打开dump文件查看处于WAITING(onobjectmonitor)的线程在做什么,比如这本书的例子中,基本都是JBOSS的工作线程在await,说明JBOSS线程池里线程池里接受的任务太少;减少JBOSS的工作线程数,找到JBOSS的线程池配置信息,将maxThreads值调小。重启JBOSS,再dump线程信息,然后统计WAITING(onobjectmonitor)的线程,发现减少了很多。=====》因为每一次从WAITING到RUNNABLE都会进行一次上下文的切换。
=====》拓展:
什么使用需要用到jstack命令dump线程信息?
我印象很深的一次就是一个lisener的项目,上线一段时间后,某个队列突然都阻塞了,消息不消费了,当时在listener admin控制台把阻塞的线程停用再启用,阻塞的消息很快都消费了,过了一段时间,这个项目的对应的另一个队列阻塞,停用再启用之后阻塞的消息也都消费了,但是有一条unacked,当时用jstack命令dump了线程信息,然后在本地启动项目,在dump线程信息,异常的线程信息和正常的线程信息对比,排查问题。====》这个是因为esl连接有问题,是怎么发现是esl的连接有问题的。想不起来了,这个记得自己研究下,做一下记录。
死锁的时候需要用这个命令dump线程信息。
2.死锁
一旦出现死锁,业务是可以感知的,因为不能继续提供服务了,只能通过dump线程查看到底是哪个线程出现了问题。
===》工作中是否遇到过线上环境死锁?我听过同事遇到过,向他请教下他是怎么知道的,怎么排查的,怎么解决问题的,做好笔记。
避免死锁的方法:
1.避免一个线程同时获取多个锁。
2.避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。
3.尝试使用定时锁,使用lock.tryLock(timeout)来替代使用内部锁机制。====》sornarlint优化的时候,遇到一个锁的优化,后来因为优化会有潜在风险,没有优化,看看那个点,对比总结下。
4.对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况。
3.资源限制的挑战
资源限制:在进行并发编程时,程序的执行速度受限于计算机硬件资源或软件资源。
硬件资源限制有带宽的上传/下载速度、硬盘读写速度和CPU的处理速度。
软件资源限制有数据库的连接数和socket连接数等。
资源限制引发的问题:并发执行的代码受资源限制串行执行,速度会更慢,增加了上下文切换和资源调度的时间。
如果解决资源限制的问题:对于硬件资源限制,可以考虑使用集群并行执行程序。对于软件资源限制,可以考虑使用资源池将资源复用。总之,根据不同的资源限制调整程序的并发数。
java并发机制的底层实现原理:
java代码在编译后会变成java字节码,字节码被类加载器加载到JVM里,JVM执行字节码,最终需要转化为汇编指令在CPU上执行,java中所使用的并发机制依赖于JVM的实现和CPU的指令。 1.volatile volatile是轻量级的synchronized,它在多处理开发中保证了共享变量的“可见性”。 volatile使用恰当的话,比synchronized使用和执行成本更低,它不会引起线程上下文的切换和调度。
先了解几个CPU术语:
未完待续。。。。
来源:oschina
链接:https://my.oschina.net/u/3944601/blog/3162790