java多线程学习之volatile 和synchronized

java多线程学习之volatile 和synchronized

安卓开发对多线程的使用很少,在平时业务开发过程中用的最多的多线程就是开启一个子线程进行数据请求操作,除此之外,很少再用到多线程。但是不用不代表着不需要了解。多线程并发是个很深奥的问题,也是很重要的知识点,所以平时虽然用的少,但是也会看关于多线程的知识点。最近学习这两个知识点,做一下总结。
在深入了解这两个属性之前,先了解3个名词。

  1. 原子性
  2. 可见性
  3. 有序性
    原子性 : 一个操作或者多个操作在执行过程中要么都执行,要么都不执行,且在执行过程中不被打断。由Java内存模型直接保证原子性的变量操作包括 read,load,assign,use,store和write,我们可以大致的认为基本数据类型的访问读写是原子操作。这里要注意,并没有plus或者minus,所以说i++,i–并不是一个原子操作,但是i=5是一个原子操作。
    可见性:可见性相对来说更容易理解些,简单来说就是一个线程改变了当前变量的值,这个值可以被其他线程立刻得知。 这里要注意的是,立刻得知的是主内存里的值,工作内存里的值是不可见的,每个线程的工作内存是独立的。除了vloatile之外,synchronized和final也可以保证可见性。
    有序性 即程序执行的顺序按照代码的先后顺序执行。volatile可防止编译器进行指令重排序

synchroized可以保证原子性,可见性以及有序性,但是volatile并不能保证原子性。这个结论大家都知道,但是看定义,既然volatile保证了可见性,(在值发生改变时侯,其他线程可以立刻得知),那为什么其他线程在取值做计算时候不能拿到最新值来计算呢?

在解释这个问题之前,我们先看一下线程的内存模型,方便我们理解java多线程学习之volatile 和synchronized_第1张图片
对于volatile修饰的变量,JVM虚拟机只能保证从主内存中加载到线程的值都是最新的。
以i++为例:A线程从主线程中获得的值是1,然后B线程进来取值,这个时候从主线程中获得i的值也是1,都没问题,这个时候i的最新值确实是1,但是这个时候A线程进行了加一操作,可是这个时候B线程已经加载了i值,不会产生对应变化,即使这个时候A线程将最新值写入到了主内存。
由于所有的变化均发生在各线程的工作内存,工作内存和主内存中变量不同步导致多线程不安全。
那么volatile的使用场景是什么呢?volatile使用只适合于单纯的赋值操作。比如以下一段代码java多线程学习之volatile 和synchronized_第2张图片
但是这个时候有人就有疑问了,以上面的理论为例:
A线程读取了shutdown参数,为false,B线程也读取了shutdown函数,此时也为false。然后A线程执行了shutdown()方法,shutdown参数被赋值为true,但是此时B线程中的shutdown参数还是false,可是为啥执行的时候又是最新值true了呢?

其实这个问题很好解释,在i++操作中,有三步操作,
1.主内存中拿值
2.自增1
3.最新值赋给i(刷新主内存中的值)
问题就出现在第二步。A线程自增1时候,B线程也可以自增1.因为此时最新值并没有刷新到主内存。
但是shutdown是个赋值(assign)操作,assign是一个原子操作,你可以理解为A线程调用了shutdown()后,B线程此时还是false。但是下一步B线程调用doWork()方法时候,由于shutdown参数已经被刷新到主内存,由于可见性,B线程意识到shutdown参数已经被更改,所以又会取最新值来使用。
如何保证原子性呢?还是使用synchroized修饰。

因为当某线程使用变量a时,其他线程无法使用变量a,只能等该线程对a操作结束,释放a的锁后才能对a进行操作。

下面将synchronized与volatile进行一下比较:
1.关键字volatile是线程同步的的轻量级实现,所以volatile肯定要比sync性能要好,并且volatile只能修饰变量,而sync可以修饰方法,变量以及代码块
2.多线程访问volatile变量不会发生阻塞,但是sync会阻塞线程
3.volatile能保证线程之间的可见性,但不能保证原子性。sync既可以保证可见性,也可以保证原子性,因为sync会将工作内存和主内存中的数据进行同步

感觉解释的还是不清楚,如有错误或者遗漏的地方,欢迎拍砖

你可能感兴趣的:(java基础知识,java相关知识)