首页 > 编程语言 >【JUC并发编程系列】深入理解Java并发机制:从用户态到内核态的探索(一、前置知识)

【JUC并发编程系列】深入理解Java并发机制:从用户态到内核态的探索(一、前置知识)

时间:2024-08-27 11:52:38浏览次数:7  
标签:JUC 态到 CAS 用户 阻塞 并发 线程 切换 内核

文章目录

【JUC并发编程系列】深入理解Java并发机制:从用户态到内核态的探索(一、前置知识)

1.用户态与内核态区别

内核态(Kernel Mode):运行操作系统程序,操作硬件 (内核空间)

用户态(User Mode):运行用户程序(用户空间)

为了安全应用程序无法直接调用的硬件的功能,而是将这些功能封装成特定的函数。当应用程序需要硬件功能时(例如读写文件),就需要进行系统调用。当进程进行系统调用后就从用户态装换为内核态。

img

比如:线程的调度需要内核态调度实现

Linux使用了Ring3级别表示用户态,Ring0标识内核态

Ring0作为内核态,没有使用Ring1和Ring2。

Ring3状态不能访问Ring0的地址 空间,包括代码和数据。、

2. 线程安全同步的方式

  • 阻塞式:synchronized/ lock(aqs) 当前线程如果没有获取到锁,当前的线程就会被阻塞等待。

  • 非阻塞式:CAS 当前线程如果没有获取到锁,不会阻塞等待 一直不断重试

T1线程获取到锁 持久时间 几毫秒 1毫秒 唤醒t2线程

T2线程没有获取到锁 直接阻塞等待

阻塞----就绪状态—cpu调度

唤醒t2线程

如果没有获取到锁,当前的线程就会被阻塞等待------重量级锁

CAS 运行用户态

3. 传统锁有哪些缺点

在使用synchronized1.0版本 如果没有获取到锁的情况下则当前线程直接会变为阻塞的

状态----升级为重量级锁,在后期唤醒的成本会非常高。

C A S ----运行用户态 缺点:cpu飙高

偏向锁 →轻量级锁(cas)→重量级锁。

轻量级都是在用户态完成;

重量级需要用户态与内核态切换;

因为:重量级锁需要通过操作系统自身的互斥量(mutex lock,也称为互斥锁)来实现,然而这种实现方式需要通过用户态和核心态的切换来实现,但这个切换的过程会带来很大的性能开销,比如:Linux内核互斥锁–mutex。

Linux内核互斥锁mutex lock:

  1. atomic_t count; //指示互斥锁的状态:

​ 1:没有上锁,可以获得;

​ 0:被锁定,不能获得。

​ 负数:被锁定,且可能在该锁上有等待进程 ,初始化为没有上锁。

  1. spinlock_t wait_lock; //等待获取互斥锁中使用的自旋锁。在获取互斥锁的过程中,操作会在自旋锁的保护中进行。初始化为为锁定。

  2. struct list_head wait_list; //等待互斥锁的进程队列。

Synchronized 获取锁的流程:需要经历 用户态与内核态切换

唤醒他:内核态阻塞—唤醒 切换用户态重新cpu调度。

申请锁时,从用户态进入内核态,申请到后从内核态返回用户态(两次切换);没有申请到时阻塞睡眠在内核态。使用完资源后释放锁,从用户态进入内核态,唤醒阻塞等待锁的进程,返回用户态(又两次切换);被唤醒进程在内核态申请到锁,返回用户态(可能其他申请锁的进程又要阻塞)。所以,使用一次锁,包括申请,持有到释放,当前进程要进行四次用户态与内核态的切换。同时,其他竞争锁的进程在这个过程中也要进行一次切换。

CPU通过分配时间片来执行任务,一个CPU在一个时刻只能运行一个线程,当一个任务的时间片用完(时间片耗尽或出现阻塞等情况),CPU会转去执行另外一个线程切换到另一个任务。在切换之前会保存上一个任务的状态(当前线程的任务可能并没有执行完毕,所以在进行切换时需要保存线程的运行状态),当下次再重新切换到该任务,就会继续切换之前的状态运行。——任务从保存到再加载的过程就是一次上下文切换

上下文切换只能发生在内核态中。内核态是 CPU 的一种有特权的模式,在这种模式下只有内核运行并且可以访问所有内存和其他系统资源。其他的程序,如应用程序,在最开始都是运行在用户态,但是他们能通过系统调用来运行部分内核的代码

而CAS 是在用户态完成而CAS基于硬件实现,不需要进入内核,不需要切换线程,操作自旋几率较少 。

4. 发生CPU上下文切换的原因

  1. 通过调用下列方法会导致自发性上下文切换:

    • Thread.sleep()
    • Object.wait()
    • Thread.yeild()
    • Thread.join()
    • LockSupport.park()
  2. 发生下列情况可能导致非自发性上下文切换:

    • 切出线程的时间片用完
    • 有一个比切出线程优先级更高的线程需要被运行
    • 虚拟机的垃圾回收动作

5. 如何避免上下文切换

  1. 无锁并发编程。多线程竞争锁时,多线程竞争锁时,加锁、释放锁会导致比较多的上下文切换;
  2. CAS算法,Java的Atomic包使用CAS算法来更新数据,而不需要加锁,可能会消耗cpu资源。
  3. 使用最少线程,避免创建不需要的线程;
  4. 协程:在单线程里实现多任务的调度,并在单线程里维持多个任务间的切换。

6. 详细总结

6.1 用户态与内核态

  • 内核态(Kernel Mode): 内核态是指操作系统运行的环境,具有最高权限,可以直接访问硬件资源,执行关键操作,如管理内存、处理器和外设等。在内核态下,操作系统可以执行任何指令。
  • 用户态(User Mode): 用户态是指应用程序运行的环境,权限较低,不能直接访问硬件资源。应用程序必须通过系统调用请求内核来执行特定操作,如文件读写、网络通信等。

当应用程序需要执行特定的硬件操作时,如读写文件或访问设备,它需要从用户态发起系统调用进入内核态。系统调用完成后,程序会回到用户态继续执行。

6.2 线程安全同步方式

  • 阻塞式同步: 使用synchronized关键字或基于AQS的锁机制实现。当一个线程试图获取一个已经被占用的锁时,该线程会被阻塞,直到锁被释放。
  • 非阻塞式同步: 使用CAS(Compare and Swap)算法实现。当线程尝试获取锁时,如果锁不可用,则线程不会被阻塞,而是继续重试直到成功。

6.3 传统锁的缺点

  • 重量级锁: 在早期的JVM实现中,如synchronized 1.0版本,如果线程无法获取锁,会直接进入阻塞状态,这会导致线程状态的转换成本较高,尤其是在锁释放后的唤醒成本。
  • 上下文切换: 重量级锁涉及从用户态到内核态的转换,这会触发上下文切换,进而影响性能。

6.4 CAS算法

  • CAS算法: CAS是一种非阻塞算法,用于实现原子操作。它由三个操作数组成:内存值(V)、旧的预期值(E)和要修改的新值(N)。当且仅当预期值E等于内存值V时,才会将内存值V更新为N。
  • 优点: CAS算法运行在用户态,不需要进入内核态,减少了上下文切换的开销。
  • 缺点: 在某些情况下可能会导致CPU空转,从而消耗大量CPU资源。

6.5 原子类(Atomic类)

  • 原子类: Java并发包提供了一系列原子类,它们利用CAS算法实现线程安全的数据更新。
    • 基本类型原子类: 如AtomicIntegerAtomicLong等。
    • 数组原子类: 如AtomicIntegerArrayAtomicLongArray等。
    • 引用类型原子类: 如AtomicReferenceAtomicStampedReference等。
    • 字段更新器: 如AtomicIntegerFieldUpdaterAtomicLongFieldUpdater等。
  • 底层实现: 原子类通常基于Unsafe类实现,通过反射获取Unsafe实例,并使用它的方法来实现CAS操作。

6.6 上下文切换

  • 发生原因: 上下文切换通常发生在线程的时间片结束、更高优先级的线程到达或者线程主动让出CPU时。
  • 避免方法:
    • 使用无锁编程技术。
    • 使用CAS算法减少锁的使用。
    • 减少线程数量,避免不必要的线程创建。
    • 使用协程技术来实现更高效的并发控制。

标签:JUC,态到,CAS,用户,阻塞,并发,线程,切换,内核
From: https://blog.csdn.net/weixin_68020300/article/details/141574500

相关文章

  • 并发上传的JS代码
    asyncfunctionuploadFile(file){constformData=newFormData();formData.append('video',file);try{constresponse=awaitfetch('YOUR_UPLOAD_URL',{method:'POST',body:for......
  • 京东面试:600Wqps高并发ID如何设计?时钟回拨 如何解决?
    文章很长,且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录博客园版为您奉上珍贵的学习资源:免费赠送:《尼恩Java面试宝典》持续更新+史上最全+面试必备2000页+面试必备+大厂必备+涨薪必备免费赠送:《尼恩技术圣经+高并发系列PDF》,帮你实现技术自由,完成职业升级,薪......
  • 【Linux】并发与并行:理解多任务处理的核心概念
    文章目录并发与并行:理解多任务处理的核心概念1.什么是并发?2.什么是并行?3.并发与并行的区别4.并发与并行的共同点与挑战5.选择并发还是并行?6.总结并发与并行:理解多任务处理的核心概念在现代计算中,性能和效率是关键问题。为了提高处理能力,计算机系统利用了并......
  • JUC7-共享模型之工具
    线程池自定义线程池importlombok.extern.slf4j.Slf4j;importorg.springframework.core.log.LogDelegateFactory;importjava.util.ArrayDeque;importjava.util.Deque;importjava.util.HashSet;importjava.util.concurrent.TimeUnit;importjava.util.concurrent......
  • 数据库系统 第23节 并发控制
    并发控制是数据库管理系统中的一个重要概念,它确保在多个用户或事务同时访问和修改数据时,数据的完整性和一致性得到维护。下面是对您提到的几种并发控制技术的详细解释和例子:锁(Locks)锁是最基本的并发控制机制之一。它通过在数据上放置锁来防止多个事务同时修改同一数据......
  • 全栈杂谈第一期:什么是计算机中的并发
    什么是计算机中的并发计算机中的“并发”是一个听起来很复杂的词汇,但我们可以把它简单理解为“同时做很多事情”。想象一下你正在做晚饭:你可以在等水烧开的时候切菜,还可以在等待炖汤时洗碗。尽管你只有一双手,但通过合理安排时间,你可以看起来像是在同时完成多项任务。这就是......
  • spring如何实现高可用高性能高并发
    在SpringBoot和SpringCloud中,可以通过以下方式实现高并发、高可用和高性能: 一、高并发 1. 异步处理-使用Spring的异步方法执行(@Async注解),将耗时的操作异步执行,不阻塞主线程,从而提高系统的并发处理能力。例如,对于一些数据处理、文件上传等操作,可以在后台异步进......
  • 高并发内存池-TCMalloc
    目录项目介绍什么是内存池定长内存池高并发内存池基数树结构申请和释放接口多线程并发环境测试全文件扩展和不足参考资料1.项目介绍1.这个项目做的是什么?当前项目是实现一个高并发的内存池,它的原型是google的一个开源项目tcmalloc,全称Thread-CachingMalloc,即线程缓......
  • 并发编程[10]_线程池
    本文介绍java中的线程池类ThreadPoolExecutor。我们可以利用ThreadPoolExecutor创建线程池,这个类中有多个构造方法。ThreadPoolExecutor(intcorePoolSize,intmaximumPoolSize,longkeepAliveTime,TimeUnitunit,BlockingQueue<Runnable>workQueue)ThreadPoolEx......
  • 并发编程[5]_wait和notify
    1.wait和notifywait()方法是Object类中的方法,他的作用是让当前线程进入等待状态,而使用notify()方法可以唤醒。wait(long):void,参数是毫秒,表示等待毫秒数,直到时间结束或被唤醒;wait(long,int):void,第一个参数是毫秒,第二个参数是纳秒,如果纳秒在0-999999之间,则第一个......