java Volatile与Synchronized的区别

引言

在研究并发程序时,我们可能都知道volatile和synchronized是用于多线程中,用于线程安全和变量可见性的,但是具体两者怎么使用,有何区别可能还是稀里糊涂一知半解,在此就自己简单的理解总结一下二者的区别,和大家一块儿学习!我们需要了解java中关键字volatile和synchronized关键字的使用以及lock类的用法。

首先,了解下java的内存模型:

java的线程内存模型中定义了每个线程都有一份自己的共享变量副本(本地内存),里面存放自己私有的数据,其他线程不能直接访问,而一些共享变量则存在主内存中,供所有线程访问。
上图中,如果线程A和线程B要进行通信,就要经过主内存,比如线程B要获取线程A修改后的共享变量的值,要经过下面两步:
 (1)、线程A修改自己的共享变量副本,并刷新到了主内存中。 
 (2)、线程B读取主内存中被A更新过的共享变量的值,同步到自己的共享变量副本中。
总结:在java内存模型中,共享变量存放在主内存中,每个线程都有自己的本地内存,当多个线程同时访问一个数据的时候,可能本地内存没有及时刷新到主内存,所以就会发生线程安全问题。

java多线程中的三个特性:

  •   原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。一个很经典的例子就是银行账户转账问题:比如从账户A向账户B转1000元,那么必然包括2个操作:从账户A减去1000元,往账户B加上1000元。这2个操作必须要具备原子性才能保证不出现一些意外的问题。
  •   可见性:当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
  •   有序性:就是程序执行的顺序按照代码的先后顺序执行。一般来说处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。如下:
int a = 20; //语句1
int r = 3; //语句2
a = a + 5; //语句3
r = a*a; //语句4

因为重排序,他的执行顺序还可能为 2 -1 - 3 - 4,1 - 3 - 2 - 4。但绝不可能 2 -1 - 4 - 3,因为这打破了依赖关系。显然重排序对单线程运行是不会有任何问题,而多线程就不一定了,所以我们在多线程编程时就得考虑这个问题了。

Volatile关键字的作用:

其实volatile关键字的作用就是保证了可见性和有序性(不保证原子性),如果一个共享变量被volatile关键字修饰,那么如果一个线程修改了这个共享变量后,其他线程是立马可知的。如果线程A修改了自己的共享变量副本,这时如果该共享变量没有被volatile修饰,那么本次修改不一定会马上将修改结果刷新到主存中,如果此时B去主存中读取共享变量的值,那么这个值就是没有被A修改之前的值。如果该共享变量被volatile修饰了,那么本次修改结果会强制立刻刷新到主存中,如果此时B去主存中读取共享变量的值,那么这个值就是被A修改之后的值了。
  volatile禁止指令重排序优化,在指令重排序优化时,在volatile变量之前的指令不能在volatile之后执行,在volatile之后的指令也不能在volatile之前执行,所以它保证了有序性。
  volatile 的读性能消耗与普通变量几乎相同,但是写操作稍慢,因为它需要在本地代码中插入许多内存屏障指令(是一种CPU指令,用于控制特定条件下的重排序和内存可见性问题。Java编译器也会根据内存屏障的规则禁止重排序。)来保证处理器不发生乱序执行。

Synchronized关键字的作用:

synchronized提供了同步锁的概念,被synchronized修饰的代码段可以防止被多个线程同时执行,必须一个线程把synchronized修饰的代码段都执行完毕了,其他的线程才能开始执行这段代码。 因为synchronized保证了在同一时刻,只能有一个线程执行同步代码块,所以执行同步代码块的时候相当于是单线程操作了,那么线程的可见性、原子性、有序性(线程之间的执行顺序)它都能保证了。synchronized并没有禁止重排序,但是synchronized相当于是一个单线程了,所以有没有重排序对程序都是没有影响的。

Volatile和synchronized的区别: 

  (1)、volatile只能作用于变量,使用范围较小。synchronized可以用在变量、方法、类、同步代码块等,使用范围比较广。
  (2)、volatile只能保证可见性和有序性,不能保证原子性。而可见性、有序性、原子性synchronized都可以包证。
  (3)、volatile不会造成线程阻塞。synchronized可能会造成线程阻塞。
  (4)、在性能方面synchronized关键字是防止多个线程同时执行一段代码,就会影响程序执行效率,而volatile关键字在某些情况下性能要优于synchronized。

什么是重排序:

  重排序是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段。但是重排序可以保证最终执行的结果是与程序顺序执行的结果一致,并且只会对不存在数据依赖性的指令进行重排序,这个重排序在单线程下对最终执行结果是没有影响的,但是在多线程下就会存在问题。

可以看一个例子: 

class TestExample {
  int a = 0;
  boolean flag = false;

 // 写入的线程
 public void writer() {
  a = 1; // 1
  flag = true; // 2

 } //读取的线程
public void reader() {
 if (flag) { // 3
 int i = a * a; // 4
 }}}

如上面代码,如果两个线程同时执行在没有发生重排序的时候int i =1,如果发生了重排序那么1,2的位置因为不存在数据依赖可以会发生位置的互换。那么这时候int i =0;当然这个在单线程是没有问题的。只有在多线程才会发生这种情况

 volatile int a = 0;
 volatile boolean flag = false;

我们只需要加上volatile关键字也是可以避免这种问题的,volatile是禁止重排序的。

什么是数据依赖?

 int a = 1;(1)
 int b = 2;(2)
 int c= a + b;(3)

这里面第三步就存在数据依赖 (依赖a和b的值)。编译器和处理器在重排序时,会遵守数据依赖性,编译器和处理器不会改变存在数据依赖关系的两个操作的执行顺序。所以这里面无论步骤(1)(2)有没有发生重排序,步骤(3)都是在他们之后执行。这里所说的数据依赖性仅针对单个处理器中执行的指令序列和单个线程中执行的操作,不同处理器之间和不同线程之间的数据依赖性不被编译器和处理器考虑。

以上就是java Volatile与Synchronized的区别的详细内容,更多关于java Volatile与Synchronized区别的资料请关注我们其它相关文章!

(0)

相关推荐

  • java多线程中的volatile和synchronized用法分析

    本文实例分析了java多线程中的volatile和synchronized用法.分享给大家供大家参考.具体实现方法如下: 复制代码 代码如下: package com.chzhao; public class Volatiletest extends Thread { private static int count = 0; public void run() {         count++;     } public static void main(String[] args) {  

  • 详解java并发编程(2) --Synchronized与Volatile区别

    1 Synchronized 在多线程并发中synchronized一直是元老级别的角色.利用synchronized来实现同步具体有一下三种表现形式: 对于普通的同步方法,锁是当前实例对象. 对于静态同步方法,锁是当前类的class对象. 对于同步方法块,锁是synchronized括号里配置的对象. 当一个代码,方法或者类被synchronized修饰以后.当一个线程试图访问同步代码块的时候,它首先必须得到锁,退出或抛出异常的时候必须释放锁.那么这样做有什么好处呢? 它主要确保多个线程在同一

  • Java关键字volatile和synchronized作用和区别

    volatile是变量修饰符,而synchronized则是作用于一段代码或方法:如下三句get代码: int i1; int geti1() {return i1;} volatile int i2; int geti2() {return i2;} int i3; synchronized int geti3() {return i3;} geti1() 得到存储在当前线程中i1的数值.多个线程有多个i1变量拷贝,而且这些i1之间可以相互不同.换句话说,另一个线程可能已经改变了它线程内的i1

  • Java线程之线程同步synchronized和volatile详解

    上篇通过一个简单的例子说明了线程安全与不安全,在例子中不安全的情况下输出的结果恰好是逐个递增的(其实是巧合,多运行几次,会产生不同的输出结果),为什么会产生这样的结果呢,因为建立的Count对象是线程共享的,一个线程改变了其成员变量num值,下一个线程正巧读到了修改后的num,所以会递增输出. 要说明线程同步问题首先要说明Java线程的两个特性,可见性和有序性.多个线程之间是不能直接传递数据交互的,它们之间的交互只能通过共享变量来实现.拿上篇博文中的例子来说明,在多个线程之间共享了Count类的

  • java中volatile和synchronized的区别与联系

    java中volatile和synchronized的区别与联系 这个可能是最好的对比volatile和synchronized作用的文章了.volatile是一个变量修饰符,而synchronized是一个方法或块的修饰符.所以我们使用这两种关键字来指定三种简单的存取变量的方式 int i1; int geti1() {return i1;} volatile int i2; int geti2() {return i2;} int i3; synchronized int geti3() {

  • java Volatile与Synchronized的区别

    引言 在研究并发程序时,我们可能都知道volatile和synchronized是用于多线程中,用于线程安全和变量可见性的,但是具体两者怎么使用,有何区别可能还是稀里糊涂一知半解,在此就自己简单的理解总结一下二者的区别,和大家一块儿学习!我们需要了解java中关键字volatile和synchronized关键字的使用以及lock类的用法. 首先,了解下java的内存模型: java的线程内存模型中定义了每个线程都有一份自己的共享变量副本(本地内存),里面存放自己私有的数据,其他线程不能直接访问

  • java多线程编程必备volatile与synchronized深入理解

    目录 Volatile概述 Synchronized概述 Volatile与Synchronized的区别 使用场景 1 Volatile的使用场景 2 Synchronized的使用场景 注意事项 相关面试问题 Volatile概述 Volatile是Java中的一种轻量级同步机制,用于保证变量的可见性和禁止指令重排.当一个变量被声明为Volatile类型时,任何修改该变量的操作都会立即被所有线程看到.也就是说,Volatile修饰的变量在每次修改时都会强制将修改刷新到主内存中,具有很好的可见

  • 简单了解java volatile关键字实现的原理

    一.volatile关键字的语义分析 1.保证可见性 对共享变量的修改,其他线程能够马上感知到.但不能保证原子性(i++) 2.保证有序性 3.volatile的原理和实现机制 有volatile修饰的共享变量进行写操作的时候会多出有 "lock"标志的汇编代码,Lock前缀的指令在多核处理器下会引发两件事情: 1)将当前处理器缓存行中的数据写回到系统内存中 2)这个写回内存的操作会使在其他cpu里缓存了该内存地址的数据无效. 二.volatile的使用场景 1.状态标志(开关模式)

  • 关于synchronized、volatile、ReentrantLock的区别与对比

    目录 并发编程特性 volatile 特性: 作用的地方: synchronized 特性 作用的地方 修饰方法,分为实例方法和静态方法 修饰代码块 ReentrantLock 特性 作用的地方 做为成员变量使用格式: 做为局部变量使用格式: 对比 聊之前先说一下并发编程的3个特性. 并发编程特性 原子性:对共享资源的一组操作,要么成功要么失败,不会出现部分成功部分失败的情况. 可见性: 当线程获取到琐时,会拷贝一份共享资源到本地内存,释放锁时会将共享资源刷新到主内存中.可见性是指当共享资源发生

  • 浅谈Java中Lock和Synchronized的区别

    目录 1. 从功能角度来看 2. 从特性来看 3. 从性能方面来看 1. 从功能角度来看 Lock和Synchronized都是java中去用来解决线程安全问题的一个工具 2. 从特性来看 Synchronized是java中的同步关键字,Lock是J.U.C包中提供的接口,而这个接口有很多的实现类,包括ReentrantLock这样重入锁的实现,Synchronized可以通过两种方式去控制锁的力度 一种把synchronized关键字修饰在方法层面,另一种是修饰在代码块上,可以通过synch

  • Java常用锁synchronized和ReentrantLock的区别

    目录 区别1:用法不同 synchronized 基础使用 ReentrantLock 基础使用 区别2:获取锁和释放锁方式不同 区别3:锁类型不同 区别4:响应中断不同 区别5:底层实现不同 小结 前言: 在 Java 中,常用的锁有两种:synchronized(内置锁)和 ReentrantLock(可重入锁),二者的功效都是相同得,但又有很多不同点,所以我们今天就来聊聊. 区别1:用法不同 synchronized 可用来修饰普通方法.静态方法和代码块,而 ReentrantLock 只

  • java并发之synchronized

    目录 1.使用方式 2.Monitor(管程) 2.1 关于管程模型 2.2 Mesa Semantics 2.3 Brinch Hanson Semantics 2.4 Hoare Semantics 2.5 monitorenter 2.6 monitorexit 3.可见性和重排序 4.局限与性能 前言: Java为我们提供了隐式(synchronized声明方式)和显式(java.util.concurrentAPI编程方式)两种工具来避免线程争用. 本章节探索Java关键字synchr

  • java volatile关键字的含义详细介绍

    java volatile关键字 在java线程并发处理中,有一个关键字volatile的使用目前存在很大的混淆,以为使用这个关键字,在进行多线程并发处理的时候就可以万事大吉. Java语言是支持多线程的,为了解决线程并发的问题,在语言内部引入了 同步块 和 volatile 关键字机制. synchronized  同步块大家都比较熟悉,通过 synchronized 关键字来实现,所有加上synchronized 和 块语句,在多线程访问的时候,同一时刻只能有一个线程能够用 synchron

随机推荐