线程同步基础

synchronized 和volatile是Java线程同步的基础。

synchronized

将临界区的内容上锁,同一时刻只有一个进程能访问该临界区代码

使用的是内置锁,锁一个时刻只能被一个线程持有,可以重入(表示一个处于synchronized代码中的线程可以进入另外一个使用synchronized的代码快,比如:方法A和方法B同时使用synchronized修饰,在方法A中调用了方法B,调用某个线程调用方法A的时候不会造成死锁,因为synchronized是可重入的锁,线程在进入方法A的时候获得了当前对象的锁,但是此时这个线程依然可以获得方法B的synchronized锁)

synchronized修饰不同对象时获得的锁:

  • 修饰普通方法:获得的锁是当前对象
  • 修饰静态方法:获得的锁是当前类class
  • 修饰代码块:取决于具体的锁对象

在Java的同步方法中synchronized是比较重量级的锁,而且不够灵活,jvm提供了更轻量的volatile,jdk提供了更灵活的Lock。

volatile

在多处理器的CPU架构下,因为每个处理器都有自己的缓存,线程访问变量的时候会读取缓存,多个线程读取的缓存不一样会导致每个线程得到的值不一样。使用该关键字的效果是:

  • 处理器将缓存写回到内存
  • 处理器将缓存写回到内存的时候会导致其他处理器的内存失效

作用

  • 保证可见性,Java内存模型(JMM)确保所有线程看到的这个变量的值是一致的
  • 只保证简单操作的原子性(保证变量简单赋值操作的原子性,如:temp = 1,不保证复杂操作的原子性,如:temp++)

内存语义(就是内存会做的操作)

  • 写:当写一个volatile的变量时,JMM把该线程对应的本地缓存中的共享变量刷新到主内存
  • 读:当读一个volatile变量时,JMM把该线程对应的本地缓存置为无效,线程接下来将从主存中读取共享变量

问题:上面提到的刷新操作是对这个本地内存刷新,还是只刷新volatile变量?

解答:是刷新整个本地缓存,包括其他共享变量

CAS

CAS:Compare And Switch,在Java中是通过调用C/C++写的本地方法完成的,C又调用了CPU的cmpxchg指令完成的。

一般来说有三个值:内存值V,期望值A,更新值B,如果内存值和期望值相等,则用更新值B替换内存值A,否则什么也不做

什么叫锁:锁其实就是维护一种状态,比如一个int状态值state,state变量对于所有线程可见,线程A将state改为1的时候(假设,当然根据具体的需要可以设为对应的值)表示上锁的状态,线程在试图修改state的时候,发现是1,说明其他线程已经改过,线程B则进入阻塞状态,当线程A释放锁的时候,也就是将state改为0的时候,唤醒线程B,线程B会重新试图获取锁,也就是修改state的值,如果state为0,那么修改成功,线程B获得锁

  • 偏向锁:为了让之前获得过该锁的线程更容易获得锁(获得锁的代价更低,不需要CAS加锁和解锁),因为Hotspot的作者研究发现:大多数情况下锁不仅不存在竞争,而且总是由同一线程多次获得

内存语义

  • 获取锁:当线程获取锁时,JMM会把线程对应的本地缓存中的共享变量刷新到主存
  • 释放锁:当线程释放锁时,JMM会把线程对应的本地缓存置为无效

对比volatile的和锁的内存语义,volatile的写——锁的获取,volatile的读——锁的释放

释放锁的线程在释放锁之前可见的变量,在获取锁的线程获取锁之后也可以看见这些变量,volatile也一样

锁的实现

  1. 利用volatile的读-写内存语义
  2. 利用CAS(CompareAndSet)附带的volatile读-写内存语义(因为在实现CAS的时候汇编会有一个lock前缀,这个前缀会带来和volatile相同的内存语义)

一些零碎的知识点:

  • JVM在类初始化阶段(Class加载后,且被线程使用前),JVM会获取一个锁,这个锁可以同步多个线程对同一个类的初始化

锁参考

CAS参考

Java 线程 — synchronized、volatile、锁的更多相关文章

  1. Java 线程安全 与 锁

    Java 线程安全 与 锁 多线程内存模型 线程私有栈内存 每个线程 私有的内存区域 进程公有堆内存 同一个进程 共有的内存区域 为什么会有线程安全问题? 多个线程同时具有对同一资源的操作权限,又发生 ...

  2. 【Java线程安全】锁

    Java都有哪些锁? synchronized 和 reentranlock是最常见的,其中前者又JVM提供实现,后者有专门对应的java.util.concurrent包提供:同时后者功能更加丰富. ...

  3. Java线程同步与锁

    一.synchronized synchronized锁什么?锁对象.可能锁对象包括: this, 临界资源对象,Class类对象. 1,同步方法 synchronized T methodName( ...

  4. Java线程安全与锁优化

    线程安全的严谨定义: 当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交题执行,也不需要进行额外的同步,或者调用方法进行其他任何操作,调用这个对象的行为都可以或者正确的结果,那么这 ...

  5. 【Thread】java线程之对象锁、类锁、线程安全

    说明: 1.个人技术也不咋滴.也没在项目中写过线程,以下全是根据自己的理解写的.所以,仅供参考及希望指出不同的观点. 2.其实想把代码的github贴出来,但还是推荐在初学的您多亲自写一下,就没贴出来 ...

  6. Java线程安全与锁优化,锁消除,锁粗化,锁升级

    线程安全的定义 来自<Java高并发实战>"当多个线程访问一个对象的时候,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方法的时候进行任何 ...

  7. 【Java线程】volatile的适用场景

    http://www.ibm.com/developerworks/cn/java/j-jtp06197.html 把代码块声明为 synchronized,有两个重要后果,通常是指该代码具有 原子性 ...

  8. java多线程synchronized volatile解析

    先简单说说原子性:具有原子性的操作被称为原子操作.原子操作在操作完毕之前不会线程调度器中断.即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行.在Java中,对除了l ...

  9. Java线程synchronized(一)

    线程安全概念:当多个线程访问某一个类(对象或方法)时,这个对象始终都能表现出正确的行为,那么这个类(对象或方法)就是线程安全的. synchronized:可以在任意对象及方法上加锁,而加锁的这段代码 ...

随机推荐

  1. LINK : fatal error LNK1104: 无法打开文件“gtestd.lib”

    解决办法: 复制编译出来的gtestd.lib文件到D:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\lib目录下 我这里用的是vs2015 ...

  2. javascript学习第一课

    1.基本知识: 1.1 词法结构 1.1.1 字符集 javascript程序是用Unicode字符集编写的. 1.1.2 区分大小写 javascript是区分大小写的语言.也即是关键字.变量.函数 ...

  3. Til the Cows Come Home

    Description Bessie is out in the field and wants to get back to the barn to get as much sleep as pos ...

  4. Repeater 使用方法

    ItemTemplate: 包含要逐一呈现给数据源中的每个数据项的 HTML 元素和控件 AlternatingItemTemplate: 包含要逐一呈现给数据源中的其他每个数据项的 HTML 元素和 ...

  5. Linux上设置nginx支持https

    1.前提条件 如果系统没有自带openssl,则需要安装. 2.生成证书 # .首先,进入你想创建证书和私钥的目录,例如: cd /etc/nginx/ # .创建服务器私钥,命令会让你输入一个口令: ...

  6. C2第七次作业解题报告

    看过题解后如果觉得还算有用,请帮忙加点我所在团队博客访问量 http://www.cnblogs.com/newbe/ http://www.cnblogs.com/newbe/p/4069834.h ...

  7. jquery 编码解码

    中文转Unicode:HttpUtility.UrlEncodeUnicode(string str); 转换后中文格式:"%uxxxx" 举例:"柳_abc123&qu ...

  8. delphi中获取调用堆栈信息

    异常堆栈有利于分析程序的错误,Delphi的Exception有StackTrace属性,但是值为空,因为StackTrace的信息收集Delphi委托给了第三方组件来完成,真是脑子有毛病! 借助于m ...

  9. C++ Base64 编码 解码

    C++实现 base64 字符串编码解码(GCC编译). /** * @brief C++ base64 编解码 * @author wid * @date 2013-20-25 * * @note ...

  10. [MSSQL]如何高效查询表的总记录数

    如何高效查询表的总记录数?[总结-整理-马克] 首先想到的自然是在表主键上应用COUNT函数来查询了,这个是目前使用最多的方法,没有之一 ) ROWS FROM product 这里再给出一些其它方法 ...