zoukankan      html  css  js  c++  java
  • JAVA并发编程4_线程同步之volatile关键字

    上一篇博客JAVA并发编程3_线程同步之synchronized关键字中讲解了JAVA中保证线程同步的关键字synchronized,其实JAVA里面还有个较弱的同步机制volatile。volatile关键字是JAVA中的轻量级的同步机制,用来将变量的更新操作同步到其他线程。从内存可见性的角度来说,写入volatile变量相当于退出同步代码块,读取volatile变量相当于进入同步代码块。

    旧的内存模型:保证读写volatile都直接发生在main memory中。

    在新的内存模型下(1.5)对volatile的语义进行了修补和增强:如果当线程 A 写入 volatile 变量 V 而线程 B 读取 V 时,那么在写入 V 时,A 可见的所有变量值现在都可以保证对 B 是可见的。

    一句话:volatile保证可见性,但不能保证原子性。

    原子性的:一组语句作为一个不可分割的单元被执行。任何一个执行同步代码块的线程,都不可能看到有其他线程正在执行由同一个锁保护的同步代码块。volatile变量的非原子性最容易被忽略。

    可见性:指一个线程修改了一个共享变量的值,其他线程能够立即得知这个修改。

    volatile的非原子性

    变量被定义为volatile并不能保证对其所有操作是原子的,由于非原子性,因此volatile并不能保证多线程并发的安全性。如下面的代码:

    public class Test implements Runnable{
    	public volatile int race = 0;
    	@Override
    	public void run() {
    		increase();
    	}
    	private void increase() {
    		race ++;
    		
    	}
    	public static void main(String[] args) {
    		Test t = new Test();
    		Thread [] threads = new Thread[1000];
    		for (int i = 0; i < 1000; i++) {
    			threads[i] = new Thread(t);
    			threads[i].start();
    		}
    		while (Thread.activeCount() > 1) {
    			Thread.yield();
    		}
    		// 保证打印的时候1000个线程都已经执行完毕
    		System.out.println(t.race);
    	}
    }
    

    这段代码开启了1000个线程,对race变量进行自增操作。理论上,线程安全的话,执行结果应该是1000。但实际上执行得到的结果都是一个小于1000的值。

    分析一下上面案的代码,问题就出在了race++这句代码。它不是原子操作。这句代码实际上是分为三个操作的:读取race的值、进行加1操作、写入新的值。

    显然可以看出来,将变量定义成vilatile也不能保证原子性:

    线程1先读取了变量race的原始值,然后线程1被阻塞了;线程2也去读取变量race的原始值,然后进行加1操作,并把+1后的值写入工作内存,最后写入主存,然后线程1接着进行加1操作,由于已经读取了race的值,此时在线程1的工作内存中race的值仍然是之前的值,所以线程1对race进行加1操作后的值和刚才一样,然后将这个值写入工作内存,最后写入主存。这样就出现了两个线程自增完后其实只加了一次。究其原因是因为volatile不能保证原子性。

    可以将自增操作改为同步代码块即可解决。

    private synchronized void increase() {
    		race ++;
    	}
    

    volatile的可见性

    一个线程修改了某个volatile变量的值,这新值对其他线程来说是立即可见的。

    boolean ready;
    // thread 1
    while (!ready) {
           doSomthing();
    }
    // thread2
    ready = true;

    这是销毁线程的通用方法。但是存在问题是ready变量改为true还没来得及写入主存,就转到其他线程执行了,这时还会进入循环。这时,volatile的作用就体现出来了。volatile变量保证了他在一个线程里面修改后会立即被其他线程得知。

    volatile变量的使用场景这篇文章写得很详细:Java 理论与实践: 正确使用 Volatile 变量

  • 相关阅读:
    重构的信号
    枚举类返回Map键值对,绑定到下拉框
    js onclick函数中传字符串参数的问题
    python opencv3 矩形 圆形边框
    python opencv3 轮廓检测
    python opencv3 滤波器 卷积核
    python opencv3 窗口显示摄像头的帧
    python opencv3 显示一张图片
    python opencv3 获取摄像头视频
    python opencv3 视频文件的读写
  • 原文地址:https://www.cnblogs.com/qhyuan1992/p/5385309.html
Copyright © 2011-2022 走看看