本人免费整理了Java高级资料,涵盖了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo高并发分布式等教程,一共30G,需要自己领取。
传送门:https://mp.weixin.qq.com/s/JzddfH-7yNudmkjT0IRL8Q

1. 三大性质简介
在并发编程中分析线程安全的问题时往往需要切入点,那就是两大核心:JMM抽象内存模型以及happens-before规则Java内存模型以及happens-before规则,三条性质:原子性,有序性和可见性。关于synchronized和volatile已经讨论过了,就想着将并发编程中这两大神器在 原子性,有序性和可见性上做一个比较,当然这也是面试中的高频考点,值得注意。

2. 原子性
原子性是指一个操作是不可中断的,要么全部执行成功要么全部执行失败,有着“同生共死”的感觉。及时在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程所干扰。我们先来看看哪些是原子操作,哪些不是原子操作,有一个直观的印象:

 int a = 10;  //
a++; //
int b=a; //
a = a+1; //

上面这四个语句中只有第1个语句是原子操作,将10赋值给线程工作内存的变量a,而语句2(a++),实际上包含了三个操作:1. 读取变量a的值;2:对a进行加一的操作;3.将计算后的值再赋值给变量a,而这三个操作无法构成原子操作。对语句3,4的分析同理可得这两条语句不具备原子性。当然,java内存模型中定义了8中操作都是原子的,不可再分的。

  1. lock(锁定):作用于主内存中的变量,它把一个变量标识为一个线程独占的状态;
  2. unlock(解锁):作用于主内存中的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
  3. read(读取):作用于主内存的变量,它把一个变量的值从主内存传输到线程的工作内存中,以便后面的load动作使用;
  4. load(载入):作用于工作内存中的变量,它把read操作从主内存中得到的变量值放入工作内存中的变量副本
  5. use(使用):作用于工作内存中的变量,它把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的值的字节码指令时将会执行这个操作;
  6. assign(赋值):作用于工作内存中的变量,它把一个从执行引擎接收到的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作;
  7. store(存储):作用于工作内存的变量,它把工作内存中一个变量的值传送给主内存中以便随后的write操作使用;
  8. write(操作):作用于主内存的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中。

上面的这些指令操作是相当底层的,可以作为扩展知识面掌握下。那么如何理解这些指令了?比如,把一个变量从主内存中复制到工作内存中就需要执行read,load操作,将工作内存同步到主内存中就需要执行store,write操作。

注意的是:java内存模型只是要求上述两个操作是顺序执行的并不是连续执行的。也就是说read和load之间可以插入其他指令,store和writer可以插入其他指令。比如对主内存中的a,b进行访问就可以出现这样的操作顺序:read a,read b, load b,load a。

由原子性变量操作read,load,use,assign,store,write,可以大致认为基本数据类型的访问读写具备原子性(例外就是long和double的非原子性协定)

synchronized
上面一共有八条原子操作,其中六条可以满足基本数据类型的访问读写具备原子性,还剩下lock和unlock两条原子操作。如果我们需要更大范围的原子性操作就可以使用lock和unlock原子操作。

尽管jvm没有把lock和unlock开放给我们使用,但jvm以更高层次的指令monitorenter和monitorexit指令开放给我们使用,反应到java代码中就是---synchronized关键字,也就是说synchronized满足原子性。
volatile 我们先来看这样一个例子:

public class VolatileExample {
private static volatile int counter = 0; public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10000; i++)
counter++;
}
});
thread.start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(counter);
}
}

开启10个线程,每个线程都自加10000次,如果不出现线程安全的问题最终的结果应该就是:10*10000 = 100000;可是运行多次都是小于100000的结果,问题在于 volatile并不能保证原子性,在前面说过counter++这并不是一个原子操作,包含了三个步骤:

1.读取变量counter的值;

2.对counter加一;

3.将新值赋值给变量counter。

如果线程A读取counter到工作内存后,其他线程对这个值已经做了自增操作后,那么线程A的这个值自然而然就是一个过期的值,因此,总结果必然会是小于100000的。
如果让volatile保证原子性,必须符合以下两条规则:

  1. 运算结果并不依赖于变量的当前值,或者能够确保只有一个线程修改变量的值;
  2. 变量不需要与其他的状态变量共同参与不变约束

3. 有序性
synchronized
synchronized语义表示锁在同一时刻只能由一个线程进行获取,当锁被占用后,其他线程只能等待。因此,synchronized语义就要求线程在访问读写共享变量时只能“串行”执行,因此synchronized具有有序性。

volatile
在java内存模型中说过,为了性能优化,编译器和处理器会进行指令重排序;也就是说java程序天然的有序性可以总结为:如果在本线程内观察,所有的操作都是有序的;如果在一个线程观察另一个线程,所有的操作都是无序的。在单例模式的实现上有一种双重检验锁定的方式(Double-checked Locking)。

代码如下:

public class Singleton {
private Singleton() { }
private volatile static Singleton instance;
public Singleton getInstance(){
if(instance==null){
synchronized (Singleton.class){
if(instance==null){
instance = new Singleton();
}
}
}
return instance;
}
}

这里为什么要加volatile了?我们先来分析一下不加volatile的情况,有问题的语句是这条:
instance = new Singleton();

这条语句实际上包含了三个操作:

1.分配对象的内存空间;

2.初始化对象;

3.设置instance指向刚分配的内存地址。

但由于存在重排序的问题,可能有以下的执行顺序:

如果2和3进行了重排序的话,线程B进行判断if(instance==null)时就会为true,而实际上这个instance并没有初始化成功,显而易见对线程B来说之后的操作就会是错得。

而用volatile修饰的话就可以禁止2和3操作重排序,从而避免这种情况。

volatile包含禁止指令重排序的语义,其具有有序性。

4. 可见性
可见性是指当一个线程修改了共享变量后,其他线程能够立即得知这个修改。通过之前对内存synchronzed语义进行了分析,当线程获取锁时会从主内存中获取共享变量的最新值,释放锁的时候会将共享变量同步到主内存中。

从而,synchronized具有可见性。同样的在volatile分析中,会通过在指令中添加lock指令,以实现内存可见性。因此, volatile具有可见性

5. 总结
通过这篇文章,主要是比较了synchronized和volatile在三条性质:原子性,可见性,以及有序性的情况,

归纳如下:
synchronized: 具有原子性,有序性和可见性; volatile:具有有序性和可见性

Java三大性质总结:原子性、可见性以及有序性的更多相关文章

  1. 「跬步千里」详解 Java 内存模型与原子性、可见性、有序性

    文题 "跬步千里" 主要是为了凸显这篇文章的基础性与重要性(狗头),并发编程这块的知识也确实主要围绕着 JMM 和三大性质来展开. 全文脉络如下: 1)为什么要学习并发编程? 2) ...

  2. 聊聊高并发(十九)理解并发编程的几种&quot;性&quot; -- 可见性,有序性,原子性

    这篇的主题本应该放在最初的几篇.讨论的是并发编程最基础的几个核心概念.可是这几个概念又牵扯到非常多的实际技术.比方Java内存模型.各种锁的实现,volatile的实现.原子变量等等,每个都可以展开写 ...

  3. Java并发编程三个性质:原子性、可见性、有序性

      并发编程 并发程序要正确地执行,必须要保证其具备原子性.可见性以及有序性:只要有一个没有被保证,就有可能会导致程序运行不正确  线程不安全在编译.测试甚至上线使用时,并不一定能发现,因为受到当时的 ...

  4. java多线程3:原子性,可见性,有序性

    概念 在了解线程安全问题之前,必须先知道为什么需要并发,并发给我们带来什么问题. 为什么需要并发,多线程? 时代的召唤,为了更充分的利用多核CPU的计算能力,多个线程程序可通过提高处理器的资源利用率来 ...

  5. Java内存模型JMM 高并发原子性可见性有序性简介 多线程中篇(十)

    JVM运行时内存结构回顾 在JVM相关的介绍中,有说到JAVA运行时的内存结构,简单回顾下 整体结构如下图所示,大致分为五大块 而对于方法区中的数据,是属于所有线程共享的数据结构 而对于虚拟机栈中数据 ...

  6. Java高并发--原子性可见性有序性

    Java高并发--原子性可见性有序性 主要是学习慕课网实战视频<Java并发编程入门与高并发面试>的笔记 原子性:指一个操作不可中断,一个线程一旦开始,直到执行完成都不会被其他线程干扰.换 ...

  7. java并发特性:原子性、可见性、有序性

    要想并发程序正确地执行,必须要保证原子性.可见性以及有序性.只要有一个没有被保证,就有可能会导致程序运行不正确. 1.原子性(Atomicity) 原子性是指在一个操作中就是cpu不可以在中途暂停然后 ...

  8. JAVA特性:原子性、可见性、有序性

    Java特性:原子性.可见性.有序性 原子性(操作是不可分.操作不可被中断):是指一个操作是不可中断的.即使是多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰.(synchronized ...

  9. Java多线程中提到的原子性和可见性、有序性

    1.原子性(Atomicity)   原子性是指在一个操作中就是cpu不可以在中途暂停然后再调度,既不被中断操作,要不执行完成,要不就不执行. 如果一个操作时原子性的,那么多线程并发的情况下,就不会出 ...

随机推荐

  1. 女朋友会 Python 是多么可怕的一件事!

    ​ 阅读文本大概需要 8 分钟. 1 目 标 场 景 女朋友会 Python 是多么可怕的一件事! 一位朋友告诉忽略了一件事,假设女朋友会 Python 的话,那岂不是要翻车?如果是这样的话,女朋友发 ...

  2. PHP 将内容写入word pdf 换行符不生效咋办

    答:把单引号换成双引号就能解析换行符(\n)了,

  3. 搭建SSM框架的配置文件

    pom.xml所需要的基本依赖和插件: <dependency>            <groupId>org.mybatis</groupId>         ...

  4. Bootstrap 基于Bootstrap和JQuery实现动态打开和关闭tab页

    基于Bootstrap和JQuery实现动态打开和关闭tab页   by:授客 QQ:1033553122 1.   测试环境 JQuery-3.2.1.min.j Bootstrap-3.3.7-d ...

  5. layui table 分页 序号始终从”1“开始解决方法

    在用Layui table 分页显示数据,用 type:"numbers" 进行显示序号有以下的问题 1.表格自带的分页,page:true 这种分页,在切换页面的时候序号可以正常 ...

  6. Cesium专栏-样条插值(平滑路径、飞行动画,源码下载)

    Cesium Cesium 是一款面向三维地球和地图的,世界级的JavaScript开源产品.它提供了基于JavaScript语言的开发包,方便用户快速搭建一款零插件的虚拟地球Web应用,并在性能,精 ...

  7. Android Gradle 学习笔记(三):Gradle 日志

    在第一节,我们使用到了gradle -q hello命令行来运行Hello World,并对Hello World进行了简单的分析,了解到 gradle -q hello 的意思是要执行的build. ...

  8. Gradle之FTP文件下载

    Gradle之FTP文件下载 1.背景 项目上需要使用本地web,所以我们直接将web直接放入assets资源文件夹下.但是随着开发进行web包越来越大:所以我们想着从版本库里面去掉web将其忽略掉, ...

  9. Data Management Technology(5) -- Recovery

    Recovery Types of Failures Wrong data entry Prevent by having constraints in the database Fix with d ...

  10. Erlang/Elixir精选Q&A

    精选的定位是什么? 已至2019年,Erlang/Elixir中文社区还是一座黑暗森林,每个技术人都怀揣着自己独有的葵花宝典独自摸索,没有一个开放的分享平台,大量优质文章没有得到该有的关注. 与此同时 ...