关键字--volatile内存可见性

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_35241080/article/details/82529542
一、volatile

volatile 关键字:当多个线程进行操作共享数据时,可以保证内存中的数据可见。

大家先来看下面一段代码发生情况:

​ 启动一个main主线程,一个子线程,子线程修改共享数据flag可终止main线程的死循环。但是由于子线程修改后main线程无法及时获取新值依然无法终止死循环,对于此情况就是内存可见性导致的问题;

​ 解决方式一:使用volatile修改共享数据flag,那么子线程修改后会立即刷新至主存,主线程使用共享数据每次都会从主存中获取,可获取最新值(推荐);

​ 解决方式二:在循环体中做运算以降低循环速度,使其可以获取最新值;

public class TestVolatile {
    public static void main(String[] args) {
        ThreadDemo td = new ThreadDemo();
        new Thread(td).start();
        int i =0;
        while(true){//运行结果会导致在这里发生死循环
//          System.out.println(i++);方式二
            if(td.isFlag()){    
              //子线程对flag的值修改,但是由于这里运行速度过快导致无法从主存中获取新值;
                System.out.println("------------------");
                break;
            }
        }
    }
}
class ThreadDemo implements Runnable {
    private volatile boolean flag = false;//方式一
    @Override
    public void run() {
        try {
            Thread.sleep(200);//导致子线程修改值操作慢于主线程
        } catch (InterruptedException e) {
        }
        flag = true;
        System.out.println("flag=" + isFlag());
    }
    public boolean isFlag() {
        return flag;
    }
    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}
二、volatile的缺点

​ 1.无法保证原子性:一个线程访问时进行读写操作完成后,其他线程才可以访问,volatile不能做到,synchronized、lock可以保证;

​ 2.无法保证互斥性:线程A访问共享数据进行修改的同时无法阻止其他线程操作共享数据

代码解释

class VolatileDemo2 implements Runnable{
    private  int i=0;
    @Override
    public void run() {
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(i++);
      //i++ 的原子性问题:i++ 的操作实际上分为三个步骤“读-改-写”
      /**
                int temp = i; 读
       *          i = i + 1;  改
       *          i = temp;   写
       */
    }
}
//main
for (int i=0;i<10;i++){
  new Thread(demo).start();
}
/**无法保证原子性同时两个线程获取到0;
0 5 8 7 6 4 3 2 1 0
*/
三、解决原子性问题

原子变量:在 java.util.concurrent.atomic 包下提供了一些原子变量。

其底层采用CAS(Compare-And-Swap) 算法保证数据变量的原子性;

CAS 算法是硬件对于并发操作的支持

CAS 包含了三个操作数:

​ ①内存值 V

​ ②预估值 A

​ ③更新值 B

​ 当且仅当 V == A 时, V = B; 否则,不会执行任何操作。

jdk1.8的java.util.concurrent.atomic下关于原子操作的方法
AtomicBoolean
AtomicInteger
AtomicIntegerArray
AtomicIntegerFieldUpdater
AtomicLong
AtomicLongArray
AtomicLongFieldUpdater
AtomicMarkableReference
AtomicReference
AtomicReferenceArray
AtomicReferenceFieldUpdater
AtomicStampedReference
DoubleAccumulator
DoubleAdder
LongAccumulator
LongAdder
Striped64

integer原子类操作

class AtomicDemo implements Runnable{
    private AtomicInteger ai = new AtomicInteger(0);
    @Override
    public void run() {
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
        }
        //获取值后再自增
        System.out.println(ai.getAndIncrement());
    }
}

//main
        AtomicDemo atomicDemo = new AtomicDemo();
        for (int i=0;i<10;i++){
           new Thread(atomicDemo).start();
       }
四、互斥性
  class SyncDemo implements Runnable{
        private int i = 0;
        @Override
        public synchronized void run() {
        //使用锁,同步代码块同样效果
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
            }
            System.out.print(i++ +",");
          //0,1,2,3,4,5,6,7,8,9,
        }
    }

//main
        SyncDemo syncDemo = new SyncDemo();
        for (int i=0;i<10;i++){
           new Thread(syncDemo).start();
       }

synchronized:保证同一时间只能有一个线程访问,相较于volatile是一个重量级的隐式锁

以上代码来源于尚硅谷juc视频:http://www.atguigu.com/

https://www.cnblogs.com/dolphin0520/p/3920373.html

猜你喜欢

转载自blog.csdn.net/qq_35241080/article/details/82529542