美文网首页
volatile关键字

volatile关键字

作者: 小李同学今天博学了吗 | 来源:发表于2020-08-06 19:27 被阅读0次
Java内存模型
截屏2020-08-04 上午9.44.53.png

简单理解就是变量存储在主内存中,当开启个新线程是会有一个工作内存,线程使用的共享内存是从主内存copy过来的,如果线程A对共享变量进行修改之后,在某个时间会将工作内存中的共享变量新值写入主内存,不存在及时写入主内存,这样在开启多个线程并访问相同变量情况下,会出现数据不正确的问题,即A线程已经改变了值,但是B线程中的工作内存里面的共享变量还是旧值。

原子性

一个操作或者多个操作,要么全部执行,要么都不执行,在Java中基本数据类型的变量的读取和赋值是原子性的,例如: x = 10;

可见性

在多线程的条件下,一个线程修改了共享变量的值,其他线程能指导能看见共享内存已经改变

有序性:

即程序执行顺序按照代码的先后顺序执行,这里为什么要说有序呢,因为CPU为了提高程序的运行效率,会对代码进行优化,它不保证执行顺序和代码的顺序一致,虽然处理器会对指令进行重排序,但是他会保证最终执行结果和顺序执行代码结果一致,

volatile关键字

1.保证可见性:
一旦一个共享变量被volatile修饰之后,那么他就具有两层含义
a.保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的
b.禁止进行指令重排序

//线程1
boolean stop = false; 
while(!stop){
    doSomething();
}
//线程2
stop = true;

这里可能会发生线程2执行完stop = true后转去做其他事了,没有将stop写入主内存,那么线程1就会一直循环

使用volatile关键字修饰后线程2修改了值后会立即写入主线程,导致线程1的缓存行无效,那么线程1就会去重新从主内存中取值,那么线程1取到的就是新的值了

2.volatile不能确保原子性

public class Nothing {
    private volatile int inc = 0;
    private volatile static int count = 10;
    private void increase() {
        ++inc;
  }
    public static void main(String[] args) {
         int loop = 10;
      Nothing nothing = new Nothing(); while (loop-- > 0) {
        nothing.operation(); 
    }
}
    private void operation() {
        final Nothing test = new Nothing();
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
              for (int j = 0; j < 1000000; j++) {
                        test.increase();
                 }
            --count;
       }).start();
    }
// 保证前面的线程都执行完 
while (count > 0) {
}

System.out.println("最后的数据为:" + test.inc); }
}

这里的结果部位100000,因为会发生这样的情况,就是线程1读取了inc的值为10,之后线程1被阻塞,线程2去读取inc的值为10,线程2进行+1 = 11,把11写入主内存,线程1切换回来之后,把11写入线程,这样两次循环就只做了一次加1的操作

解决方法:通过sychronized或lock进行加锁

3.volatile保证了有序性
volatile关键字禁止指令重排序有两层意思:
1)当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定 全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行;
2)在进行指令优化时,不能将在对volatile变量的读操作或者写操作的语句放在其 后面执行,也不能把volatile变量后面的语句放到其前面执行。

乐观锁和悲观锁

悲观锁:
当一个线程被挂起时,加入到阻塞队列,在一定的时间或条件下,在通过 notify(),notifyAll()唤醒回来。 在某个资源不可用的时候,就将cpu让出,把当前等 待线程切换为阻塞状态。等到资源(比如一个共享数据)可用了,那么就将线程唤 醒,让他进入runnable状态等待cpu调度,典型的就是synchronized,他修饰的数据同一时刻只能被一个线程访问,其他访问的线程进入到挂起状态

乐观锁:
每次不加锁而是假设修改数据之前 其他线程一定不会修改,如果因为修改过产生冲突就失败就重试,直到成功为止。 在上面的例子中,某个线程可以不让出cpu,而是一直while循环,如果失败就重 试,直到成功为止,典型技术CAS,当同步冲突很少的时候CAS的效率很高,但是他存在三个问题:(1.ABA问题,就是当变量从A-B-A 时,会误认为没有改变值,其实他已经改变了,解决这个问题我们加入了版本号,2.循环时间开销大问题,3.只能保证一个共享变量的原子操作问题)

相关文章

网友评论

      本文标题:volatile关键字

      本文链接:https://www.haomeiwen.com/subject/gvbirktx.html