概述

用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的更多相关文章

  1. JAVA多线程之synchronized和volatile实例讲解

    在多线程中,提到线程安全.线程同步,我们经常会想到两个关键字:volatile和synchronized,那么这两者有什么区别呢? 1. volatile修饰的变量具有可见性 volatile是变量修 ...

  2. (二)java多线程之synchronized

    本人邮箱: kco1989@qq.com 欢迎转载,转载请注明网址 http://blog.csdn.net/tianshi_kco github: https://github.com/kco198 ...

  3. JAVA多线程之Synchronized关键字--对象锁的特点

    一,介绍 本文介绍JAVA多线程中的synchronized关键字作为对象锁的一些知识点. 所谓对象锁,就是就是synchronized 给某个对象 加锁.关于 对象锁 可参考:这篇文章 二,分析 s ...

  4. JAVA多线程之Synchronized、wait、notify实例讲解

    一.Synchronized synchronized中文解释是同步,那么什么是同步呢,解释就是程序中用于控制不同线程间操作发生相对顺序的机制,通俗来讲就是2点,第一要有多线程,第二当多个线程同时竞争 ...

  5. Java多线程之synchronized及其优化

    Synchronized和同步阻塞synchronized是jvm提供的同步和锁机制,与之对应的是jdk层面的J.U.C提供的基于AbstractQueuedSynchronizer的并发组件.syn ...

  6. Java多线程之synchronized详解

    目录 synchronized简介 同步的原理 对象头与锁的实现 锁的优化与升级 Monitor Record 锁的对比 synchronized简介 synchronized关键字,一般称之为&qu ...

  7. Java多线程之synchronized(四)

    前面几章都是在说synchronized用于对象锁,无论是修饰方法也好修饰代码块也好,然而关键字synchronized还可以应用到static静态方法上,如果这样写,那就是对当前的*.java文件所 ...

  8. Java多线程之synchronized(三)

    在多线程访问同一个对象中的不同的synchronized方法或synchronized代码块的前提下,也就是“对象监控器”为同一个对象的时候,也就是synchronized的锁为同一把锁的时候,调用的 ...

  9. java 多线程之synchronized wait/notify解决买票问题

    一.Java线程具有五中基本状态 新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread(); 就绪状态(Runnable):当调用线程对象的st ...

随机推荐

  1. Idea生成代码段

    使用快捷键(ctrl+alt+s)找到:从idea的菜单File->Settings->Editor->Live Templates 先添加Template Group,然后添加Li ...

  2. MySql基础补漏笔记

    在MySQL教程|菜鸟教程系统复习的时候有一些知识点还没掌握透的或者思维方式还没完全跟上的地方,写了一个笔记,讲道理此笔记对除我之外的任何读者不具有任何实用价值,只针对我在复习MySQL基础过程中的查 ...

  3. 【HDU - 2859 】Phalanx (dp 最大对称子图)

    Phalanx 先搬翻译 Descriptions: 给你一个矩阵,只由小写或大写字母构成.求出它的最大对称子矩阵的边长. 其中对称矩阵是一个k*k的矩阵,它的元素关于从左下角到右上角的对角线对称.例 ...

  4. StarUML之三、StarUML的项目

    1:创建空的项目 创建项目可以按Ctrl+N或选择菜单File| New,StarUML安装打开后默认会有个空项目结构. 2:创建模板项目 可以通过选择模板来启动建模项目(会根据模板创建项目结构). ...

  5. OpenResty 在马蜂窝广告监测中的应用

    马蜂窝技术原创内容,更多干货请订阅公众号:mfwtech 广告是互联网变现的重要手段之一. 以马蜂窝旅游 App 为例,当用户打开我们的应用时,有可能会在首屏或是信息流.商品列表中看到推送的广告.如果 ...

  6. 开源工作流管理系统节点接收人设置“指定节点处理人”系列讲解

    关键字: 驰骋工作流程快速开发平台 工作流程管理系统 工作流引擎 asp.net工作流引擎 java工作流引擎. 开发者表单  拖拽式表单 工作流系统CCBPM节点访问规则接收人规则 适配数据库: o ...

  7. MySQL服务器的运维与优化

    MySQL运维 安装数据库 配置本地yum源,将gpmall-repo文件上传至/opt目录 创建yum.repo文件 安装mariadb服务 # yum install -y mariadb mar ...

  8. idea如何做到多模块开发项目 收藏整理

    idea如何做到多模块开发项目 <packaging>pom</packaging>是什么意思? idea 快捷键汇总

  9. Anaconda使用教程全攻略

    Anaconda使用教程全攻略 本文转自 https://zhuanlan.zhihu.com/p/32925500           〇.序 Python是一种面向对象的解释型计算机程序设计语言, ...

  10. Django之F与Q查询

    F查询 from django.db.models import F,Q # 1.查询库存数大于卖出数的书籍 res = models.Book.objects.filter(kucun__gt=F( ...