Java实现多线程的上下文切换

网友投稿 273 2022-11-19


Java实现多线程的上下文切换

对于上下文切换不同的操作系统模式也不尽相同,这里我们只讨论Unix系统,在我之前的文章中提到过windows的抢占式,这里就不在赘述。

无论是单核还是多核CPU都是支持多线程代码的,CPU通过给每个线程分配CPU时间片来实这个机制。

时间片是CPU分配给各个线程的时间,因为时间片非常短,所以CPU通过不停地切换线程执行,让我们感觉多个线程是同时执行的,时间片一般是几十毫秒(ms)

CPU通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务。但是,在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再加载这个任务的状态。

所以任务从保存到再加载的过程就是一次上下文切换。 很明显上下文切换会影响多线程的执行速度。

如何减少上下文切换

减少上下文切换的方法有

1、无锁并发编程。

多线程竞争锁时,会引起上下文切换,所以多线程处理数据时,可以用一

些办法来避免使用锁,如将数据的ID按照Hash算法取模分段,不同的线程处理不同段的数据。

2、CAS算法。

java的Atomic包使用CAS(compare and swap)算法来更新数据,而不需要加锁。

3、使用最少线程。避免创建不需要的线程,比如任务很少,但是创建了很多线程来处理,这

样会造成大量线程都处于等待状态。

4、协程:在单线程里实现多任务的调度,并在单线程里维持多个任务间的切换。

减少上下文切换的例子

下面我们看一个通过减少线上大量WAITING的线程,来减少上下文切换次数的例子:

使用jstack命令dump线程信息,看看pid为3117的进程里的线程都在做什么

sudo -u admin /opt/java/bin/jstack 31177 > /home/java/dump17

统计所有线程分别处于什么状态,发现300多个线程处于WAITING(onobjectmonitor)状态

grep java.lang.Thread.State dump17 | awk '{print $2$3$4$5}'

| sort | uniq -c

39 RUNNABLE

21 TIMED_WAhttp://ITING(onobjectmonitor)

6 TIMED_WAITING(parking)

51 TIMED_WAITING(sleeping)

305 WAITING(onobjectmonitor)

3 WAITING(parking)

打开dump文件查看处于WAITING(onobjectmonitor)的线程在做什么。发现这些线程基本全是JBOSS的工作线程,在await。说明JBOSS线程池里线程接收到的任务太少,大量线程都闲着。

"http-0.0.0.0-7001-97" daemon prio=10 tid=0x000000004f6a8000 nid=0x555e in

Object.wait() [0x0000000052423000]

java.lang.Thread.State: WAITING (on object monitor)

at java.lang.Object.wait(Native Method)

- waiting on <0x00000007969b2280> (a org.apache.tomcat.util.net.AprEndpoint$Worker)

at java.lang.Object.wait(Object.java:485)

at org.apache.tomcat.util.net.AprEndpoint$Worker.await(AprEndpoint.java:1464)

- locked <0x00000007969b2280> (a org.apache.tomcat.util.net.AprEndpoint$Worker)

at org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1489)

at java.lang.Thread.run(Thread.java:662)

减少JBOSS的工作线程数,找到JBOSS的线程池配置信息,将maxThreads降到100

emptySessionPath="false" minSpareThreads="40" maxSpareThreads="75"

maxPostSize="512000" protocol="HTTP/1.1"

enableLookups="false" redirectPort="8443" acceptCount="200" bufferSize="16384"

connectionTimeout="15000" disableUploadTimeout="false" useBodyEncodingForURI= "true">

重启JBOSS,再dump线程信息,然后统计WAITING(onobjectmonitor)的线程,发现减少了175个。

WAITING的线程少了,系统上下文切换的次数就会少,因为每一次从WAITTING到RUNNABLE都会进行一次上下文的切换。

读者也可以使用vmstat命令测试一下。

grep java.lang.Thread.State dump17 | awk '{print $2$3$4$5}'

| sort | uxIzRrmniq -c

44 RUNNABLE

22 TIMED_WAITING(onobjectmonitor)

9 TIMED_WAITING(parking)

36 TIMED_WAITING(sleeping)

130 WAITING(onobjectmonitor)

1 WAITING(parking)

为什么要减少上下文切换

当CPU从执行一个线程切换到执行另外一个线程的时候,它需要先存储当前线程的本地的数据,程序指针等,然后载入另一个线程的本地数据,程序指针等,最后才开始执行。

这种切换称为“上下文切换”(“context switch”)。

CPU会在一个上下文中执行一个线程,然后切换到另外一个上下文中执行另外一个线程。上下文切换并不廉价,是比较耗时的

emptySessionPath="false" minSpareThreads="40" maxSpareThreads="75"

maxPostSize="512000" protocol="HTTP/1.1"

enableLookups="false" redirectPort="8443" acceptCount="200" bufferSize="16384"

connectionTimeout="15000" disableUploadTimeout="false" useBodyEncodingForURI= "true">

重启JBOSS,再dump线程信息,然后统计WAITING(onobjectmonitor)的线程,发现减少了175个。

WAITING的线程少了,系统上下文切换的次数就会少,因为每一次从WAITTING到RUNNABLE都会进行一次上下文的切换。

读者也可以使用vmstat命令测试一下。

grep java.lang.Thread.State dump17 | awk '{print $2$3$4$5}'

| sort | uxIzRrmniq -c

44 RUNNABLE

22 TIMED_WAITING(onobjectmonitor)

9 TIMED_WAITING(parking)

36 TIMED_WAITING(sleeping)

130 WAITING(onobjectmonitor)

1 WAITING(parking)

为什么要减少上下文切换

当CPU从执行一个线程切换到执行另外一个线程的时候,它需要先存储当前线程的本地的数据,程序指针等,然后载入另一个线程的本地数据,程序指针等,最后才开始执行。

这种切换称为“上下文切换”(“context switch”)。

CPU会在一个上下文中执行一个线程,然后切换到另外一个上下文中执行另外一个线程。上下文切换并不廉价,是比较耗时的


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:IDEA自定义常用代码块及自定义快捷摸板
下一篇:Spring boot开发web应用JPA过程解析
相关文章

 发表评论

暂时没有评论,来抢沙发吧~