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_WAITING(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

<maxThreads="250" maxHttpHeaderSize="8192"
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 | uniq -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会在一个上下文中执行一个线程,然后切换到另外一个上下文中执行另外一个线程。上下文切换并不廉价,是比较耗时的

以上这篇Java实现多线程的上下文切换就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Java实现多线程轮流打印1-100的数字操作

    首先打印1-100数字如果用一个单线程实现那么只要一个for循环即可,那么如果要用两个线程打印出来呢?(一个线程打印奇数,一个线程打印偶数)于是大家会想到可以通过加锁实现,但是这样的效率是不是不高?这里我用一个变量来控制两个线程的输出 public class ThreadTest { volatile int flag=0; public void runThread() throws InterruptedException{ Thread t1=new Thread(new Thread1

  • java多线程之Future和FutureTask使用实例

    Executor框架使用Runnable 作为其基本的任务表示形式.Runnable是一种有局限性的抽象,然后可以写入日志,或者共享的数据结构,但是他不能返回一个值. 许多任务实际上都是存在延迟计算的:执行数据库查询,从网络上获取资源,或者某个复杂耗时的计算.对于这种任务,Callable是一个更好的抽象,他能返回一个值,并可能抛出一个异常.Future表示一个任务的周期,并提供了相应的方法来判断是否已经完成或者取消,以及获取任务的结果和取消任务. public interface Callab

  • JAVA多线程间通讯常用实现方法解析

    如何实现线程间通讯,有如下三种方法: 1.使用Semaphore (信号量)类来控制线程的等待和释放 功能:三个线程 a .b .c 并发运行,b,c 需要 a 线程的数据怎么实现 分析:考虑到多线程的不确定性, 因此我们不能确保 ThreadA 就一定先于 ThreadB 和 ThreadC 前执行,就算 ThreadA先执行了, 我们也无法保证 ThreadA 什么时候才能将变量 num 给初始化完成. 因此我们必须让 ThreadB 和 Thread去等待 ThreadA 完成任何后发出的

  • java利用多线程和Socket实现猜拳游戏

    本文实例为大家分享了利用多线程和Socket实现猜拳游戏的具体代码,供大家参考,具体内容如下 实例:猜拳游戏 猜拳游戏是指小时候玩的石头.剪刀.布的游戏.客户端与服务器的"较量",服务器会自动随机产生出石头.剪刀或者布,客户端则由用户输入石头.剪刀.布之一,然后服务器通过比较告知比较结果.比如,客户端出石头,而服务器出的是剪刀,那么,服务器就会告知客户端,用户赢了.猜拳游戏也要制作一个多线程的程序,让多个客户端能够共同参与游戏.在该程序中,要包括猜拳的业务逻辑的线程类.客户端类.服务器

  • Java多线程通信:交替打印ABAB实例

    使用wait()和notify()实现Java多线程通信:两个线程交替打印A和B,如ABABAB public class Test { public static void main(String[] args) { final PrintAB print = new PrintAB(); new Thread(new Runnable() { public void run(){ for(int i=0;i<5;i++) { print.printA(); } } }).start(); n

  • 浅析 Java多线程

    什么是进程 当一个程序进入内存中运行起来它就变为一个进程.因此,进程就是一个处于运行状态的程序.同时进程具有独立功能,进程是操作系统进行资源分配和调度的独立单位. 什么是线程 线程是进程的组成部分.通常情况下,一个进程可拥有多个线程,而一个线程只能拥有一个父进程. 线程可以拥有自己的堆栈.自己的程序计数器及自己的局部变量,但是线程不能拥有系统资源,它与其父进程的其他线程共享进程中的全部资源,这其中包括进程的代码段.数据段.堆空间以及一些进程级的资源(例如,打开的文件等). 线程是进程的执行单元,

  • Java中的多线程一定就快吗?

    并发编程与多线程编程 要了解并发编程,首先要懂得与并行这个概念进行区分.并行是指两个事件同时进行,并发是CPU切换速度快,看起来像是每个任务同时进行一样.多线程是实现并发编程的一种方式,假设一个场景,在广州地铁高峰时段,一群人涌进地铁里,在不同的闸机口刷卡进去.在这个场景里,进地铁就是任务,每个人可以看出是并发的,而多个刷卡闸机口就是多线程.   并发编程的本质目的是为了充分利用CPU,让程序运行得更快.然而,并不是启动更多的线程就能让程序最大限度地并发执行.在进行并发编程时,如果希望通过多线程

  • Java 多线程传值的四种方法

    其实大家都知道多线程传值有三种方式: 1:通过构造方法传递数据 2:通过变量和方法传递数据 3:通过回调函数传递数据 那么博主有个非常变态的需求,所以找出了第四种实现方式,先看效果图: 动态Cron4j调度器,我曾经发过类似的文章,可以去搜索一下. 点击执行走下边的代码,然后根据类名反编译 public static void executeCron4j(String packageClass){ try { Object taskObj = classNewInstance(packageCl

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

    对于上下文切换不同的操作系统模式也不尽相同,这里我们只讨论Unix系统,在我之前的文章中提到过windows的抢占式,这里就不在赘述. 无论是单核还是多核CPU都是支持多线程代码的,CPU通过给每个线程分配CPU时间片来实这个机制. 时间片是CPU分配给各个线程的时间,因为时间片非常短,所以CPU通过不停地切换线程执行,让我们感觉多个线程是同时执行的,时间片一般是几十毫秒(ms) CPU通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务.但是,在切换前会保存上一个任务的

  • 详解Java中多线程异常捕获Runnable的实现

    详解Java中多线程异常捕获Runnable的实现 1.背景: Java 多线程异常不向主线程抛,自己处理,外部捕获不了异常.所以要实现主线程对子线程异常的捕获. 2.工具: 实现Runnable接口的LayerInitTask类,ThreadException类,线程安全的Vector 3.思路: 向LayerInitTask中传入Vector,记录异常情况,外部遍历,判断,抛出异常. 4.代码: package step5.exception; import java.util.Vector

  • Java实现多线程文件下载的代码示例

    Java实现多线程文件下载思路: 1.基本思路是将文件分段切割.分段传输.分段保存. 2.分段切割用到HttpUrlConnection对象的setRequestProperty("Range", "bytes=" + start + "-" + end)方法. 3.分段传输用到HttpUrlConnection对象的getInputStream()方法. 4.分段保存用到RandomAccessFile的seek(int start)方法. 5

  • 详解Java实现多线程的三种方式

    本文实例为大家分享了Java实现多线程的三种方式,供大家参考,具体内容如下 import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; public class Main { public static void main(String[] args) { //方法一:继承Thread int i = 0; // for(; i < 100; i++){ // System.out.println(T

  • Java 实现多线程的几种方式汇总

    我们先来看段示例代码 import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; public class Main { public static void main(String[] args) { //方法一:继承Thread int i = 0; // for(; i < 100; i++){ // System.out.println(Thread.currentThread().getNa

  • java实现多线程的两种方式继承Thread类和实现Runnable接口的方法

    实现方式和继承方式有什么区别呢? *区别: *继承Thread:线程代码存放在Thread子类run方法中 *实现Runnable:线程代码存放在接口的子类的run方法中 *实现方式的好处:避免了单继承的局限性 *在定义线程时,建议使用实现方式,当然如果一个类没有继承父类,那么也可以通过继承Thread类来实现多线程 *注意:Runnable接口没有抛出异常,那么实现它的类只能是try-catch不能throws *Java对多线程的安全问题提供了专业的解决方式就是同步代码块synchroniz

  • java 中多线程生产者消费者问题详细介绍

    java 中多线程生产者消费者问题 前言: 一般面试喜欢问些线程的问题,较基础的问题无非就是死锁,生产者消费者问题,线程同步等等,在前面的文章有写过死锁,这里就说下多生产多消费的问题了 import java.util.concurrent.locks.*; class BoundedBuffer { final Lock lock = new ReentrantLock();//对象锁 final Condition notFull = lock.newCondition(); //生产者监视

  • java 实现多线程的方法总结

    java 实现多线程的三种方法 在java中,有三种方法可以实现多线程.第一种方法:继承Thread类,重写run函数.第二种方法:实现Runnable接口,重写run函数.第三种方法:实现Callable接口,重写call函数.本文章将通过实例讲解这三种方法如何实现多线程.需要的可以参考一下.  (1)继承Thread类,重写run函数. class xx extends Thread{ public void run(){ Thread.sleep(1000) //线程休眠1000毫秒,sl

  • Java Thread多线程详解及用法解析

    最全面的java多线程用法解析,如果你对Java的多线程机制并没有深入的研究,那么本文可以帮助你更透彻地理解Java多线程的原理以及使用方法. 1.创建线程 在Java中创建线程有两种方法:使用Thread类和使用Runnable接口.在使用Runnable接口时需要建立一个Thread实例.因此,无论是通过Thread类还是Runnable接口建立线程,都必须建立Thread类或它的子类的实例.Thread构造函数: public Thread( ); public Thread(Runnab

  • Java Thread多线程全面解析

    多线程是java中很重要的知识点,在此小编给大家总结Java Thread多线程,非常有用,希望大家可以掌握哦. 一.线程的生命周期及五种基本状态 关于Java中线程的生命周期,首先看一下下面这张较为经典的图: 上图中基本上囊括了Java中多线程各重要知识点.掌握了上图中的各知识点,Java中的多线程也就基本上掌握了.主要包括: Java线程具有五种基本状态 新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread(); 就绪状态(Runnab

随机推荐