【Java并发篇】什么是多线程中的上下文切换?

在这里插入图片描述

多线程中的上下文切换

  • ✔️ 简述
  • ✔️拓展知识仓
    • ✔️减少上下文切换

✔️ 简述


上下文切换是指 CPU 从一个线程转到另一个线程时,需要保存当前线程的上下文状态,恢复另一个线程的上下文状态,以便于下一次恢复执行该线程时能够正确地运行。


在多线程编程中,上下文切换是一种常见的操作,上下文切换通常是指在一人 CPU 上,由于多个线程共享 CPU时间片,当一个线程的时间片用完后,需要切换到另一个线程运行。此时需要保存当前线程的状态信息,包括程序计数器、寄存器、栈指针等,以便下次继续执行该线程时能够恢复到正确的执行状态。同时,需要将切换到的线程的状态信息恢复,以便于该线程能够正确运行。


在多线程中,上下文切换的开销比单线程大,因为在多线程中,需要保存和恢复更多的上下文信息。过多的上下文切换会降低系统的运行效率,因此需要尽可能减少上下文切换的次数。


演示一个上下文切换场景,Code.1:



/**
*    这个示例使用了synchronized关键字和wait、notify方法来控制线程之间的同步
*/
public class ComplexContextSwitchExample {  
  
    private int count = 0;  
    private final Object lock = new Object();  
  
    public static void main(String[] args) {  
        ComplexContextSwitchExample example = new ComplexContextSwitchExample();  
        Thread thread1 = new Thread(example::increment, "Thread 1");  
        Thread thread2 = new Thread(example::increment, "Thread 2");  
  
        thread1.start();  
        thread2.start();  
    }  
  
    public void increment() {  
        for (int i = 0; i < 1000; i++) {  
            synchronized (lock) {  
                count++;  
                System.out.println(Thread.currentThread().getName() + " incremented count to " + count);  
                // 释放锁,以便其他线程可以获取锁并执行  
                lock.notify();  
            }  
            // 让出CPU执行权,以便其他线程有机会执行  
            Thread.yield();  
        }  
    }  
}

在这个Demo中,有一个共享变量count和一个锁对象lock。两个线程(线程1和线程2)都试图同时访问和修改count变量。为了确保线程安全,我们使用synchronized关键字和lock对象来同步对count变量的访问。在每个线程的increment方法中,我们在增加count变量之后调用lock.notify()方法来唤醒等待在锁上的其他线程。然后,我们使用Thread.yield()方法让出CPU执行权,以便其他线程有机会执行。这样,我们就模拟了一个更复杂的上下文切换场景,其中涉及到了线程同步和等待/通知机制。

✔️拓展知识仓


✔️减少上下文切换


频繁的上下文切换会导致CPU时间的浪费,因此在多线程编程时需要尽可能地避免它。以下是一些避免频繁上下文切换的方法:


  1. 减少线程数:可以通过合理的线程池管理来减少线程的创建和销毁,线程数不是越多越好,合理的线程数可以避免线程过多导致上下文切换。

  2. 使用无锁并发编程:无锁并发编程可以避免线程因等待锁而进入阻塞状态,从而减少上下文切换的发生。

  3. 使用CAS算法: CAS算法可以避免线程的阻塞和唤醒操作,从而减少上下文切换。

  4. 使用协程(JDK 19的虚拟线程): 协程是一种用户态线程,其切换不需要操作系统的参与,因此可以避免上下文切换。 (避免的是操作系统级别的上下文切换,但是仍然需要在JVM层面做一些保存和恢复线程的状态,但是也成本低得多)

  5. 合理地使用锁:在使用锁的过程中,需要避免过多地使用同步块或同步方法,尽量缩小同步块或同步方法的范围,从而减少线程的等待时间,避免上下文切换的发生。

先来看第一个减少上下文切换的Code.2:


/**
*    使用了线程池来管理线程,以便更有效地复用线程资源
*/

import java.util.concurrent.ExecutorService;  
import java.util.concurrent.Executors;  
  
public class ContextSwitchReductionExample {  
  
    public static void main(String[] args) {  
        int numThreads = 10;  
        ExecutorService executor = Executors.newFixedThreadPool(numThreads);  
  
        for (int i = 0; i < numThreads; i++) {  
            executor.execute(new Task());  
        }  
  
        executor.shutdown();  
    }  
}  
  
class Task implements Runnable {  
    @Override  
    public void run() {  
        // 执行任务的代码  
        System.out.println(Thread.currentThread().getName() + " is running.");  
    }  
}

在这个Demo中,使用Java的ExecutorService来创建一个固定大小的线程池。我们将要执行的Task对象提交给线程池,线程池会为每个任务分配一个线程来执行。由于线程池的大小是固定的,因此线程可以在任务之间复用,从而减少了上下文切换的次数。与直接创建多个线程相比,使用线程池可以更有效地管理线程资源,并减少上下文切换的开销。


先来看第贰个减少上下文切换的Code.3:


import java.util.concurrent.*;  

/**
*    使用了线程池和Future来异步执行任务,并使用ExecutorCompletionService来获取任务的结果。
*/
public class ComplexContextSwitchReductionExample {  
  
    public static void main(String[] args) throws Exception {  
        ExecutorService executor = Executors.newFixedThreadPool(5);  
        ExecutorCompletionService<String> completionService = new ExecutorCompletionService<>(executor);  
  
        for (int i = 0; i < 10; i++) {  
            final int taskId = i;  
            completionService.submit(() -> {  
                // 执行任务的代码  
                return "Task " + taskId + " completed.";  
            });  
        }  
  
        for (int i = 0; i < 10; i++) {  
            Future<String> future = completionService.take(); // 获取下一个完成的任务结果  
            System.out.println(future.get()); // 获取任务返回值  
        }  
  
        executor.shutdown();  
    }  
}

在这个Demo中,使用Java的ExecutorService来创建一个固定大小的线程池。然后,我们使用ExecutorCompletionService来异步提交任务并获取任务的结果。通过使用ExecutorCompletionService,我们可以按任务完成的顺序获取结果,而不是等待每个任务完成。这样可以减少线程之间的上下文切换,并提高程序的效率。同时,我们还使用了Future来获取任务的返回值,这使得我们可以轻松地处理任务的异步执行结果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/273659.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

嵌入式面试准备

题目都摘于网上 嵌入式系统中经常要用到无限循环&#xff0c;如何用C编写死循环 while(1){}或者for(;&#x1f609; 内存分区 代码区&#xff0c;全局区&#xff08;全局变量&#xff0c;静态变量&#xff0c;以及常量&#xff09;&#xff0c;栈区&#xff0c;堆区 const关键…

[SWPUCTF 2021 新生赛]sql

[SWPUCTF 2021 新生赛]sql wp 输入 1 正常回显&#xff1a; ?wllm1 返回&#xff1a; Want Me? Cross the Waf Your Login name:xxx Your Password:yyy输入单引号引发报错&#xff1a; ?wllm1 返回&#xff1a; Want Me? Cross the Waf You have an error in your SQL s…

指点云 宁波大带宽大存储云服务器 性能评测

指点云 浙江宁波 云服务器 性能测评&#xff0c;2核4G 100兆带宽 240G存储 5G防御 仅需76.8元/月&#xff1b;4核8G仅需112.8元/月。 官网地址&#xff1a;https://url.vpszj.cn/zhidianyun 优惠注册地址&#xff1a;https://url.vpszj.cn/zhidianyun_r 性价比高的服务器推荐…

【产品设计】零代码核心模块之三:报表

报表的用于数据和信息呈现的一种方式&#xff0c;能够帮助人们更直观地了解数据和信息&#xff0c;从而做出更明智的决策。本文从报表的价值、产品功能以及数据统计分析出发&#xff0c;探讨报表的重要性与使用场景&#xff0c;希望对你有所启发。 报表适用于需要呈现数据或信息…

Spring高手之路-Spring支持的注入方式、Spring为什么不建议使用基于字段的依赖注入

目录 Spring支持的注入方式 1.字段注入 2.构造器注入 3.setter注入 使用构造器注入存在的问题 Spring为何不建议使用基于字段的依赖注入 1.单一职责问题 2.可能产生NPE&#xff08;空指针异常&#xff09; 3.隐藏依赖 4.不利于测试 Spring支持的注入方式 1.字段注入 A…

k8s的二进制部署: 源码包部署-----node节点部署

服务器IP软件包k8s--master0120.0.0.61kube-aplserver&#xff0c;kube-controer-manager&#xff0c;kube-scheduler&#xff0c;etcdk8s--master0220.0.0.62kube-controer-manager&#xff0c;kube-schedulernode节点0120.0.0.62kubelet&#xff0c;kube-proxy&#xff0c;et…

【http】HTTP/1.0、HTTP/1.1和HTTP/2.0

✨ 专栏介绍 在当今互联网时代&#xff0c;计算机网络已经成为了人们生活和工作中不可或缺的一部分。而要实现计算机之间的通信和数据传输&#xff0c;就需要依靠各种网络协议来进行规范和约束。无论是浏览网页、发送电子邮件还是进行在线交流&#xff0c;都离不开各种各样的网…

2023年航天大事件

2023年&#xff0c;中国完成宇航发射近70次&#xff0c;是中国航天新的里程碑。中国科技工作者继续推进航天科技创新&#xff0c;并在运载火箭发动机研制固体燃料火箭研发、可重复航天器研发等方面取得重大突破&#xff1b;继续推进载人航天工程、北斗工程等中国重大航天旗舰工…

HOJ 项目部署-前端定制 默认勾选显示标签、 在线编辑器主题和字号大小修改、增加一言功能 题目AC后礼花绽放

# 项目拉取地址&#xff1a; https://gitee.com/himitzh0730/hoj.git # 切换到hoj-vue目录执行以下命令 #安装依赖 npm install #运行服务 npm run serve #修改代码后构建项目到dist文件夹&#xff0c;到服务器docker-compose.yml中修改hoj-frontend文件映射即可 npm run build…

python进阶 — Python解释器

1、Python解释器 Python解释器是一个计算机程序&#xff0c;它将Python代码转换为计算机可以理解的机器代码&#xff0c;并执行这些机器代码。 1. 这篇文章介绍如何下载和安装Python解释器&#xff1a; python基础&#xff08;2&#xff09;— 环境搭建 2 . 这篇文章介绍如…

果然,大厂都在卷这个!

大家好&#xff0c;我是鱼皮。首先祝大家平安夜快乐&#xff01;给大家看看我们搞的小圣诞树哈哈&#xff5e; 言归正传&#xff0c;这周中我去北京待了 2 天&#xff0c;主要是收到百度的邀请去参加百度云的智算大会&#xff0c;听说有些 AI 产品要发布。 我自己是非常关注国内…

C#教程(四):多态

1、介绍 1.1 什么是多态 在C#中&#xff0c;多态性&#xff08;Polymorphism&#xff09;是面向对象编程中的一个重要概念&#xff0c;它允许不同类的对象对同一消息做出响应&#xff0c;即同一个方法可以在不同的对象上产生不同的行为。C#中的多态性可以通过以下几种方式实现…

每日一题-----逆序字符串

大家好我是Beilef&#xff0c;在一个美好的下午我意外接触到编程并且产生了兴趣&#xff0c;哈哈我要努力成为一个跨界者&#xff0c;让我们一起加油吧O(∩_∩)O 文章目录 目录 文章目录 前言 大家好请上车 一、逆序字符串 题⽬描述&#xff1a; 输⼊⼀个字符串&#xff0c;写…

UiPath报告 | 2024年7大自动化和Al趋势

在自动化与人工智能的合力推动下&#xff0c;全球企业正在步入一个创造价值的新纪元。 预计市场将涌现出一系列高潜力的AI应用实例&#xff1b; 企业的日常运营和复杂流程管理方式将面临根本性的变革&#xff0c;人工智能的崛起正加速企业自动化进程&#xff0c;促成价值的快速…

【WPF】使用Behavior以及ValidationRule实现表单校验

文章目录 使用ValidationRule实现检测用户输入EmptyValidationRule 非空校验TextBox设置非空校验TextBox设置非空校验并显示校验提示 结语 使用ValidationRule实现检测用户输入 EmptyValidationRule是TextBox内容是否为空校验&#xff0c;TextBox的Binding属性设置ValidationRu…

Centos7:Jenkins+gitlab+node项目启动(1)

安装Jenkins 虚拟机配置 需要的软件 https://download.csdn.net/download/myy2012/88668255 解压到目录 用xftp 上传 开始安装jdk rmp -ivh jdk-8u181-linux-x64.rpm 开始安装jenkins rmp -ivh jenkins-2.99-1.1.noarch.rpm 修改用户与端口(端口按需修改) vim /etc/sy…

Matlab之State Flow

打开方式 方式一&#xff1a;在命令窗口输入State Flow或者简写sf就能打开&#xff0c;并且会自动打开State Flow 的Library。从左到右分别是图表、真值表、状态转换表、例子、顺序查看&#xff0c;可以加入到Simulink当中。 方式二&#xff1a;从Simulink Library里面添加Sta…

Xshell 从github克隆项目:使用ssh方式。

接上文&#xff1a; https://blog.csdn.net/liu834189447/article/details/135247868 是能克隆项目了&#xff0c;但是速度太磕碜了&#xff0c;磕碜到难以直视。 找到另外一种办法&#xff0c;使用SSH克隆项目 速度嘎嘎猛。 首先得能进得去github网站&#xff0c;不能点上边…

Pikachu靶场 “Http Header”SQL注入

1. 先在 pikachu 打开 Http Header 注入模块&#xff0c;点击提示 查看登录 账号 和 密码&#xff0c;登陆后去 Burp 中找到登陆的 GET请求 2. 设置payload1 &#xff1a;在 User-Agent最后 输入 查看 数据库名 or updatexml(1,concat(0x7e,database()),0) or 查看 用户名…

File Inclusion(Pikachu)

File Inclusion(local) 这里随便点击一个提交 观察url&#xff0c;显示是一个文件file1.php 可以直接通过url修改这个文件 找到自己的文件&#xff08;本地文件&#xff09;shell.php的路径写上去 就可以看到 File Inclusion&#xff08;remote&#xff09; 提交的是一个目标…