上下文切换

一、什么是上下文切换

即使是单核处理器也支持多线程执行代码,CPU通过给每个线程分配CPU时间片来实现这个机制。时间片是CPU分配给各个线程的时间,因为时间片非常短,所以CPU通过不停地切换线程执行,让我们感觉多个线程是同时执行的,时间片一般是几十毫秒(ms)。 CPU通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个 任务。但是,在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再加载这 个任务的状态。所以任务从保存到再加载的过程就是一次上下文切换

二、如何减少上下文切换

减少上下文切换的方法有无锁并发编程CAS算法使用最少线程使用协程

  • 无锁并发编程:多线程竞争锁时,会引起上下文切换,所以多线程处理数据时,可以用一 些办法来避免使用锁,如将数据的ID按照Hash算法取模分段,不同的线程处理不同段的数据。
  • ·CAS算法:Java的Atomic包使用CAS算法来更新数据,而不需要加锁。
  • 使用最少线程:避免创建不需要的线程,比如任务很少,但是创建了很多线程来处理,这 样会造成大量线程都处于等待状态。
  • 协程:在单线程里实现多任务的调度,并在单线程里维持多个任务间的切换。

三、CAS算法

CAS全称为Compare And Swap即比较并交换,其算法公式如下:
函数公式:CAS(V,E,N)

  • V:表示要更新的变量
  • E:表示预期值
  • N:表示新值

CAS原理图

java 上下文存对象 java上下文切换_java 上下文存对象


如果V值等于E值,则将V的值设为N。若V值和E值不同,则说明已经有其他线程做了更新,则当前线程什么都不做。通俗的理解就是CAS操作需要我们提供一个期望值,当期望值与当前线程的变量值相同时,说明还没线程修改该值,当前线程可以进行修改,也就是执行CAS操作,但如果期望值与当前线程不符,则说明该值已被其他线程修改,此时不执行更新操作,但可以选择重新读取该变量再尝试再次修改该变量,也可以放弃操作。

CAS的缺点:

  1. CPU开销较大:在并发量比较高的情况下,如果许多线程反复尝试更新某一个变量,却又一直更新不成功,循环往复,会给CPU带来很大的压力。
  2. 不能保证代码块的原子性(多个变量):CAS机制所保证的只是一个变量的原子性操作,而不能保证整个代码块的原子性。比如需要保证3个变量共同进行原子性的更新,就不得不使用Synchronized了。
  3. ABA问题

什么是ABA问题
CAS 算法实现一个重要前提需要取出内存中某时刻的数据,而在下时刻比较并替换,那么在这个时间差里数据可能会发生变化。
比如说一个线程1从内存位置 V 中取出 A,这时候另一个线程2 也从内存中取出 A,并且线程2 进行了一些操作使得A变成了 B,然后 线程2又将 V 位置的数据变成 A,这时候线程1进行 CAS 操作发现内存中仍然是 A,然后线程1 操作成功。尽管线程1 的 CAS 操作成功,但是不代表这个过程就是没有问题的。

如何解决ABA问题
部分乐观锁的实现是通过版本号(version)的方式来解决 ABA 问题,乐观锁每次在执行数据的修改操作时,都会带上一个版本号,一旦版本号和数据的版本号一致就可以执行修改操作并对版本号执行+1 操作,否则就执行失败。因为每次操作的版本号都会随之增加,所以不会出现 ABA 问题,因为版本号只会增加不会减少。在java中对应的两个类:

  • AtomicStampedReference
  • AtomicMarkableReference