概述:所谓的原子性是指一次操作或者多次操作中,要么所有的操作全部都得到了执行并且不会受到任何因素的干扰而中断,要么所有的操作都不执行

  1. class VolatileAtomivThread implements Runnable{
  2. private int count = 0;
  3. @Override
  4. public void run() {
  5. for (int x = 0; x<100; x++){
  6. count++;
  7. System.out.println(count);
  8. }
  9. }
  10. }
  11. class VolatileAtomicThreadDemo{
  12. public static void main(String[] args) {
  13. VolatileAtomivThread thread = new VolatileAtomivThread();
  14. for (int i = 0; i < 100; i++) {
  15. new Thread(thread).start();
  16. }
  17. }
  18. }

执行结果:不保证一定是10000

问题原理说明:
以上问题主要发生在count++操作上

count++操作包含三个步骤
从主内存中读取数据到工作内存
对工作内存中的数据进行++操作
将工作内存中的数据写回主内存
count++操作不是一个原子性操作,也就是说在某一时刻对某一个操作的执行,有可能被其他的线程打断
image.png
1.假设此时x的值是100,线程A需要对改变量进行自增1的操作,首先它需要从主内存中读取变量x的值,由于CPU的切换关系,此时CPU的执行权被切换到了B线程,A线程就处于就绪状态B线程处于运行状态
2.线程B也需要从主内存中读取x变量的值,由于线程A没有对x的值做任何修改因此此时B读取到的数据还是100
3.线程B工作内存x执行了+1操作,但是未刷新至主内存中
4.此时CPU的执行权切换到了A线程上,由于此时线程B没有将工作内存中的数据刷新到主内存,因此A线程工作内存中的变量值还是100,没有失效
5.线程B将101写入到主内存
6.线程A将101写入到主内存
虽然计算了2此,但是只对A进行了一次修改

volatile原子性测试

  1. private volatile int count = 0;

在多线程环境下,volatile关键字可以保证共享数据的可见性,但是并不能保证对数据操作的原子性(在多线程环境下volatile修饰的变量也是线程不安全的)
在多线程环境下,要保证数据的安全性,我们还需要使用锁机制

volatile的使用场景
开关控制 :利用可见性,控制某一段代码执行或者关闭
多个线程操作共享变量,但是是有一个线程对其进行写操作,其他的线程都是读

问题解决
使用锁机制

  1. class VolatileAtomivThread implements Runnable{
  2. private volatile int count = 0;
  3. private static final Object obj = new Object();
  4. @Override
  5. public void run() {
  6. for (int x = 0; x<100; x++){
  7. synchronized (obj) {
  8. count++;
  9. System.out.println(count);
  10. }
  11. }
  12. }
  13. }

原子类

概述:java从JDK1.5开始提供了java.util.concurrent.atomic包(简称Atomic包),这个包中的原子操作类提供了一种用法简单,性能高效,线程安全的更新一个变量的方式

AtomicInteger
原子性Integer,可以实现原子更新操作

改造 案列

  1. class VolatileAtomivThread implements Runnable {
  2. private AtomicInteger atomicInteger = new AtomicInteger();
  3. @Override
  4. public void run() {
  5. for (int i = 0; i < 100; i++) {
  6. int i1 = atomicInteger.getAndIncrement();
  7. System.out.println(i1);
  8. }
  9. }

原子类CAS机制实现线程安全
CAS全称是:Compare And Swap(比较再交换);是现代CPU广泛支持的一种对内存中的共享数据进行操作的一种特殊指令,CAS可以将read-modify-check-write转换为原子操作,这个原子操作直接由处理器运行
CAS机制当中使用了3个基本操作数:内存地址V,旧的预期值A,要修改的新值B
image.png
image.png
image.png
image.png

CAS与Synchronized
CAS和Synchronized都可以保证多线程环境下共享数据的安全性,那么他们两者有什么区别?
Synchronized是从悲观的角度出发
总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁
(共享资源每次只给一个线程使用,其他线程阻塞,用完后再把资源转让给其他线程),因此Synchronized我们也将其称之为悲观锁,jdk中的Reentrantlock也是一种悲观锁,性能较差
CAS是从乐观的角度出发
总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据
CAS这种机制我们也可以将其称之为乐观锁,综合性能较好