Java多线程之synchronized和volatile
概述
用Java来开发多线程程序变得越来越常见,虽然Java提供了并发包来简化多线程程序的编写,但是我们有必要深入研究一下,才能更好的掌握这块知识。
本文主要对Java提供的底层原语synchronized和volatile进行分析,看看他们究竟干了什么,以及怎么样才能合理的使用它们。
运算速度与IO速度的问题
现代计算机模型,待计算的数据主要存储在内存中,CPU想要对数据进行计算,就必须要经过下面的流程:从内存读取数据-->CPU计算-->把计算结果写回内存。但是,不得不承认一个事实,就是CPU与内存之间的IO速度,要比CPU的运算速度慢很多,所以这样就不能充分发挥CPU的计算能力。
为了解决这个问题,引入了高速缓存的概念,它一般位于内存与CPU之间,与CPU之间有较高的IO速度。高速缓存存放常用的数据,这样就可以大幅度提高CPU的利用率。
由缓存命中问题引出的指令重排序
上面提到了高速缓存之间的概念。既然提到高速缓存,那么就要谈到缓存命中的问题,如果缓存命中率很高,那么整体性能就会提升。所以,试想一下,有下面几行代码:
int a = 0;
a = a + 10;
int b = 0;
b = b + 5;
a = a * 2;
通常我们会认为,执行完第2条代码后,a被写入到高速缓存,然后执行对b的操作,最后再从高速缓存中读取出a,再对a做乘法计算。
但是,现代编译器都会对这种情况做优化。考虑一个问题,既然两次对a操作的指令之间,没有使用到a的指令,那么为什么不在对a做完加法后,直接再对a做个乘法呢。反正又不会影响整个代码的语义,而且还能避免高速缓存不命中的问题,万一这之间执行的代码很多,然后a被清理出了高速缓存,那么到最后执行对a的乘法时,又要从内存中读取,这样看来,很不划算,所以就出现了指令重排。就是说指令真正的执行顺序,不一定是按照代码书写的顺序执行的,可能会打乱顺序执行,但是只要不影响整段代码的语义就行了,因为它们是“好像是串行执行”的方式。
比如下面的代码,如果对a的操作真的发生在对b的操作之前,那么就改变了整段代码的语义,所以这时候就不会重排指令。
int a = 0;
a = a + 10;
int b = 0;
b = b + a;
a = a * 2;
Java的存储(内存)结构
内存中,主要分为两大块区域:
1、全局区域,所有线程共享该区域。
2、线程私有区域,存放需要使用的全局数据的副本。
存储结构:
第一层,位于CPU内部的高速缓存,为了解决CPU与内存之间IO速度和CPU执行速度之间差距太大的问题。
第二层:内存(包括全局区域和线程私有区域)。
对一个变量的操作,通常需要经过下面的这8个步骤(如果不是同步操作,没有1和8)。见下图的蓝色字。

多线程要考虑的问题
1、内存一致性
某个变量,它在主内存中的值,应该和在线程工作内存中的值是一致的。
2、内存可见性
某个变量,如果一个线程对它进行修改,那么其他线程应该能立即看到它的变化。
3、有序性
如果一个线程A依赖另一个线程B的执行结果,那么在线程B看来是串行执行的指令(其实可能经过了指令重排),在线程A看来,就是一个错误的执行顺序。比如下面的情况:
| Thread 1 | Thread 2 |
|---|---|
| x = 1; | int r1 = y; |
| y = 2; | int r2 = x; |
有可能出现 r1 = 2 、r2 = 0 的情况,因为Thread1可能会进行指令重排,所以对于Thread2来说,就发生了错误。
synchronized工作原理
synchronized关键字,通过第1步,对主内存中变量加锁的操作(lock),获得了该变量的使用权,随后,其他线程没有访问这个被加锁变量的权利,一直到该线程使用完成,然后解锁该变量(unlock),之后其他线程才能访问该变量,当然也能看到该变量最新的结果。
由于synchronized以一种让上述8个步骤原子执行的方式工作,所以,它解决了内存一致性的问题,内存可见性的问题、有序性的问题。
使用场景:所有在有多线程共享数据的地方,都可以使用,简单粗暴,但是会引降低性能。
volatile工作原理
volatile关键字,通俗点来说,就是对一个变量的操作,2(read) 、3(load)、 4(use)这3个操作必须是原子的,而
5(assign)、6(store)、7(write)这3个操作也必须是原子的。
那么我们来看看,它能解决什么问题,因为简单来说就是,读(2 3 4)和写(5 6
7)操作分别都是原子的,相当于CPU每次都是直接和内存交互,所以高速缓存就变得无效了。而高速缓存变得无效,那么基本上,也就没有指令重排了。而由于每次改动都会直接写到内存,每次使用都从内存读取新的数据,所以也就满足了内存可见性。但是由于在读和写之间,其他线程也可以进行读和写,那么还是会出现内存一致性问题的。所以它解决了内存可见性、有序性、但是不能解决内存可见性。
使用场景:由于volatile的特性,主要可以使用在下面的场景
1、不依赖于变量之前的状态的,比如一个 volatile boolean 来在一个线程中控制另一个线程的运行。
2、禁止指令重排,比如一个线程依赖于另一个线程真正的顺序执行结果,而不是语义上的顺序执行(其实是经过指令重排的)。
Java多线程之synchronized和volatile的更多相关文章
- JAVA多线程之synchronized和volatile实例讲解
在多线程中,提到线程安全.线程同步,我们经常会想到两个关键字:volatile和synchronized,那么这两者有什么区别呢? 1. volatile修饰的变量具有可见性 volatile是变量修 ...
- (二)java多线程之synchronized
本人邮箱: kco1989@qq.com 欢迎转载,转载请注明网址 http://blog.csdn.net/tianshi_kco github: https://github.com/kco198 ...
- JAVA多线程之Synchronized关键字--对象锁的特点
一,介绍 本文介绍JAVA多线程中的synchronized关键字作为对象锁的一些知识点. 所谓对象锁,就是就是synchronized 给某个对象 加锁.关于 对象锁 可参考:这篇文章 二,分析 s ...
- JAVA多线程之Synchronized、wait、notify实例讲解
一.Synchronized synchronized中文解释是同步,那么什么是同步呢,解释就是程序中用于控制不同线程间操作发生相对顺序的机制,通俗来讲就是2点,第一要有多线程,第二当多个线程同时竞争 ...
- Java多线程之synchronized及其优化
Synchronized和同步阻塞synchronized是jvm提供的同步和锁机制,与之对应的是jdk层面的J.U.C提供的基于AbstractQueuedSynchronizer的并发组件.syn ...
- Java多线程之synchronized详解
目录 synchronized简介 同步的原理 对象头与锁的实现 锁的优化与升级 Monitor Record 锁的对比 synchronized简介 synchronized关键字,一般称之为&qu ...
- Java多线程之synchronized(四)
前面几章都是在说synchronized用于对象锁,无论是修饰方法也好修饰代码块也好,然而关键字synchronized还可以应用到static静态方法上,如果这样写,那就是对当前的*.java文件所 ...
- Java多线程之synchronized(三)
在多线程访问同一个对象中的不同的synchronized方法或synchronized代码块的前提下,也就是“对象监控器”为同一个对象的时候,也就是synchronized的锁为同一把锁的时候,调用的 ...
- java 多线程之synchronized wait/notify解决买票问题
一.Java线程具有五中基本状态 新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread(); 就绪状态(Runnable):当调用线程对象的st ...
随机推荐
- idea快速创建一个类 实现一个接口
一 创建一个接口类 二 点击接口名称 按alt + ent 三 选择implement interface 选项 完美!!!!!!!
- 面向对象之包装类与Object类
一.包装类(Wrapper) 1.包装类出现原因 为了使8种基本数据类型的变量具有类的特征,引入包装类 2.类型间转化 ① 基本数据类型---->对应的包装类:自动装箱 包装类---->对 ...
- Metasploit学习笔记(一) Samba服务 usermap_script安全漏洞相关信息
一.Samba介绍 Samba是linux和unix系统上实现smb协议的一个免费软件,由客户机和服务器构成.SMB是一种在局域网上实现共享文件和打印机的协议.存在一个服务器,客户机通过该协议可以服务 ...
- docker配置容器运行jar包
拉取jdk镜像文件 # docker pull huanwei/alpine-oraclejdk8 创建文件夹编写Dockerfile文件 # mkdir docker # vi Dockerfile ...
- ES6学习笔记(一):轻松搞懂面向对象编程、类和对象
目录 面向过程编程P OP(Process oriented programming) 面向对象编程OOP(Object Oriented Programming) 总结 @ 面向过程编程P OP(P ...
- 学习MVC框架,处理分页和删除分页转跳的问题
第一次写博客,文采不好请多见谅,这里主要是写一下,自己是如何处理分页问题,我想初学者也遇到过这个问题. 分页的情况下,编辑信息有返回和编辑2个按钮,操作后都是应该返回原分页界面,使用TempData把 ...
- 最新版的EF Core对UWP支持的怎么样
为啥写这篇帖子呢?其实是因为翻微软的文档中心偶然翻到的,于是就出于好奇就试试了,看看用着怎么样. 以前没注意图片,所以我今天发现的时候,显示EF Core3.1支持standard2.0,于是就想试试 ...
- 13.Android-ListView使用、BaseAdapter/ArrayAdapter/SimpleAdapter适配器使用
1.ListView ListView 是 Android 系统为我们提供的一种列表显示的一种控件,使用它可以用来显示我们常见的列表形式.继承自抽象类 AdapterView.继承图如下所示: 以微信 ...
- 掌握这13个MySQL索引知识点,让你面试通过率翻倍
数据库索引有关的知识,说实在的,真的是很复杂,本来想好好看看这方面的东西,然后写篇文章详细谈谈的,后来发现索引的知识太难太深,要谈得全面又详细真的很难,所以最后还是把自己学到的和想到的变成下面一个个的 ...
- proptypes介绍
开始 prop-types的主要作用:对props中数据类型进行检测及限制 引用方法:import PropTypes from 'prop-types' 用法: // 基本用法 用来检测数据类型 c ...