1 案例引出可见性

代码解析:新起一个子线程执行m()方法,1秒后主线程将b置为false,子线程是否会停止执行死循环while(b){},打印“end”

package com.bernardlowe.concurrent.t01;

import java.util.concurrent.TimeUnit;

public class Test_09 {

    boolean b = true;

    void m(){
        System.out.println("start");
        while(b){}
        System.out.println("end");
    }

    public static void main(String[] args) {
        final Test_09 t = new Test_09();
        new Thread(new Runnable() {
            @Override
            public void run() {
                t.m();
            }
        }).start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        t.b = false;
    }

}

结果:1秒钟过后并不会停止执行死循环while(b){},打印“end”
这时候,如果将boolean b = true;这段代码前加一个volatile关键字
volatile boolean b = true;,就会达到预想中的效果


思考:为什么加上这个关键字,其他线程就会读取到已经改变的变量的值了?

是因为在CPU计算过程中,会将计算过程需要的数据加载到CPU计算缓存中,当CPU计算中断时,有可能刷新缓存,重新读取内存中的数据。在线程运行的过程中,如果某变量被其他线程修改,可能造成数据不一致的情况,从而导致结果错误。
而volatile修饰的变量是线程可见的,当JVM解释volatile修饰的变量时,会通知CPU,在计算过程中,每次使用变量参与计算时,都会检查内存中的数据是否发生变化,而不是一直使用CPU缓存中的数据,可以保证计算结果的正确。

但是这样还有一个问题,volatile只能保证可见性,不能保证原子性

2 案例引出原子性

下面再看一个示例:
预期结果:起10个线程,每个线程都对count增加10000,预期结果为count=100000

package com.bernardlowe.concurrent.t01;

import java.util.ArrayList;
import java.util.List;

public class Test_10 {

    volatile int count = 0;
    /*synchronized*/ void m(){
        for(int i = 0; i < 10000; i++){
            count++;
        }
    }

    public static void main(String[] args) {
        final Test_10 t = new Test_10();
        List<Thread> threads = new ArrayList<>();
        for(int i = 0; i < 10; i++){
            threads.add(new Thread(new Runnable() {
                @Override
                public void run() {
                    t.m();
                }
            }));
        }
        for(Thread thread : threads){
            thread.start();
        }
        for(Thread thread : threads){
            try {
                thread.join();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        System.out.println(t.count);
    }
}

但结果并不是


原因是volatile只是通知底层计算时,CPU检查内存数据,而不是让一个变量在多个线程中同步。

这时候可以给m()方法增加一个synchronized关键字,可以达到预期的效果,即synchronized void m()

还有另一种方法可以保证原子性,在上面代码将count声明为AtomicInteger原子操作,结果仍然是100000

    // 其中的每个方法都是原子操作。可以保证线程安全。
    AtomicInteger count = new AtomicInteger(0);
    void m(){
        for(int i = 0; i < 10000; i++){
            count.incrementAndGet();
        }
    }

这里不仅仅可声明Integer类型,java.util.concurrent.atomic包里面还有其他类型的

多线程总结-同步之volatile关键字的更多相关文章

  1. zz剖析为什么在多核多线程程序中要慎用volatile关键字?

    [摘要]编译器保证volatile自己的读写有序,但由于optimization和多线程可以和非volatile读写interleave,也就是不原子,也就是没有用.C++11 supposed会支持 ...

  2. java架构之路(多线程)JMM和volatile关键字(二)

    貌似两个多月没写博客,不知道年前这段时间都去忙了什么. 好久以前写过一次和volatile相关的博客,感觉没写的那么深入吧,这次我们继续说我们的volatile关键字. 复习: 先来简单的复习一遍以前 ...

  3. JAVA多线程学习- 三:volatile关键字

    Java的volatile关键字在JDK源码中经常出现,但是对它的认识只是停留在共享变量上,今天来谈谈volatile关键字. volatile,从字面上说是易变的.不稳定的,事实上,也确实如此,这个 ...

  4. java多线程(4)---volatile关键字

    volatile关键字 一旦一个共享变量(类的成员变量.类的静态成员变量)被volatile修饰之后,那么就具备了两层语义: 1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的 ...

  5. java架构之路(多线程)JMM和volatile关键字

    说到JMM大家一定很陌生,被我们所熟知的一定是jvm虚拟机,而我们今天讲的JMM和JVM虚拟机没有半毛钱关系,千万不要把JMM的任何事情联想到JVM,把JMM当做一个完全新的事物去理解和认识. 我们先 ...

  6. 多线程总结-同步之synchronized关键字

    目录 1.为什么要使用synchronized? 2.synchronized锁什么,加锁的目的是什么? 3.代码示例 3.1锁this和临界资源对象 3.2锁class类对象 3.3 什么时候锁临界 ...

  7. Java多线程初学者指南(6):慎重使用volatile关键字

    volatile关键字相信了解Java多线程的读者都很清楚它的作用,和sychnorized 一样用于多线程的同步.volatile关键字用于声明简单类型变量,如int.float.boolean等数 ...

  8. Java 并发:volatile 关键字解析

    摘要: 在 Java 并发编程中,要想使并发程序能够正确地执行,必须要保证三条原则,即:原子性.可见性和有序性.只要有一条原则没有被保证,就有可能会导致程序运行不正确.volatile关键字 被用来保 ...

  9. java并发系列(六)-----Java并发:volatile关键字解析

    在 Java 并发编程中,要想使并发程序能够正确地执行,必须要保证三条原则,即:原子性.可见性和有序性.只要有一条原则没有被保证,就有可能会导致程序运行不正确.volatile关键字 被用来保证可见性 ...

随机推荐

  1. windows下,Qt Creator 中javascript调试器安装并使用

    最开始使用Qt Creator时,想使用断点来调试javascript代码.但在按下debug键后,却提示调试器未配置,让我比较郁闷. 好了,郁闷的是说了,咱们来说说高兴的.要Qt Creator调试 ...

  2. .NET中的GC总结

    来自<CLR via C# 3rd Edition>总结 只管理内存,非托管资源,如文件句柄,GDI资源,数据库连接等还需要用户去管理 循环引用,网状结构等的实现会变得简单.GC的标志也压 ...

  3. MySQL 常用数据存储引擎区别

    mysql有多种存储引擎,目前常用的是 MyISAM 和 InnoDB 这两个引擎,除了这两个引擎以为还有许多其他引擎,有官方的,也有一些公司自己研发的.这篇文章主要简单概述一下常用常见的 MySQL ...

  4. 树莓派 Qt5.7交叉编译

    一.准备软件    1.2016-11-25-raspbian-jessie.img(官网下载)    2.cross-compile-tools-master.zip    3.gcc-4.7-li ...

  5. C语言宏定义##连接符和#符的使用(MFC就是靠##自动把消息和消息函数对应起来了,借助宏来减少switch case代码的编写量)

    C语言中如何使用宏C(和C++)中的宏(Macro)属于编译器预处理的范畴,属于编译期概念(而非运行期概念).下面对常遇到的宏的使用问题做了简单总结. 关于#和## 在C语言的宏中,#的功能是将其后面 ...

  6. kubernetes实战篇之部署一个.net core微服务项目

    目录 继上一篇kubernetes理论知识完结.本篇主要讲解基于nexus搭建一个docker镜像仓库(当然大家实践过程是不必完全跟着做,也可以搭建harbor仓库或者直接把镜像推送到docker h ...

  7. vi的替换使用、如何让linux有回收站功能、系统重要文件、目录数据

      1 vi的替换使用方法 vi使用的原理 (编辑文件会生成一个隐藏临时文件) 1.1 替换文件内容方法:vi (1)%s#oldboy#oldgirl#g --- 将oldboy全部替换为oldgi ...

  8. 浅谈AI视频技术超分辨率

    泛娱乐应用成为主流,社交与互动性强是共性,而具备这些特性的产品往往都集中在直播.短视频.图片分享社区等社交化娱乐产品,而在这些产品背后的黑科技持续成为关注重点,网易云信在网易MCtalk 泛娱乐创新峰 ...

  9. HBase 学习之路(四)—— HBase集群环境配置

    一.集群规划 这里搭建一个3节点的HBase集群,其中三台主机上均为Regin Server.同时为了保证高可用,除了在hadoop001上部署主Master服务外,还在hadoop002上部署备用的 ...

  10. 【docker学习一】CentOS7.5+Docker安装及使用「安装、查看、pull、创建、进入镜像」

    记录安装配置以及使用的过程,可能会有多处摘抄,已注明照抄地址,侵删. 是什么:个人理解,是一种移植性很强的虚拟机,支持版本控制(类似于git),同一个服务器可以运行多个docker容器,每个docke ...