线程安全的严谨定义:

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

java共享数据分类(5类

1)不可变

2)绝对线程安全:不管运行环境如何,调用者都不需要任何额外的同步措施,java api中标注自己是线程安全的类,都不是绝对线程安全的

3)相对线程安全:就是我们通常意义上讲的线程安全,需要保证对这个对象的单独操作是线程安全的,我们在调用的时候不需要做额外的保障措施,但对于一些特定顺序的连续调用就需要调用端使用额外的保障措施,比如vector的线程安全的容器,其add,get,size方法都被synchronized修饰,但是当另外一个线程恰好在错误的时间删除一个元素,导致该元素已经不再可用的话,就会产生异常,余姚对删除元素操作锁定一下

4)线程兼容:指对象本身不是线程安全的,但是可以通过在调用端正确的使用同步手段保证对象在多线程环境下是线程安全的,我们平常说的一个类不是线程安全的,通常指的就是这种情况

5)线程对立:无论是否采取同步措施,都无法并发执行,比如两个不同的线程同时持有一个线程对象,一个尝试去中断线程,一个尝试去恢复线程,这种情况无论是否采取同步措施,都无法并发执行,还存在死锁的风险

线程安全的实现方法:

1.互斥同步(悲观锁,最大的问题就是线程阻塞和唤醒带来的性能问题)

1.1最基本的互斥同步就是synchroized关键字(可重入锁,非公平锁)(重量级锁,线程阻塞唤醒开销大)

一点优化:在通知系统阻塞线程前加入一段自旋等待过程,避免频繁切换到核心态

1.2 ReentrantLock(也是重入锁,默认下非公平锁),需要lock,unlock方法配合try/finally完成操作,相比于synchronized,ReentrantLock增加了3个高级功能:可中断,可实现公平锁,以及锁可以绑定多个条件

注意:多线程环境下,synchronized的吞吐量下降得厉害,而ReentrantLock则能基本保证在一个稳定水平,是因为synchronized还要很多优化的余地!!

2.非阻塞同步(乐观锁,基于冲突检测的乐观并发策略)

通俗的说,就是先进行操作,如果没有其他线程竞争共享数据,那操作就成功了,如果共享数据有争用,产生了冲突,就是采取其他措施(最常见的就是不断的重试,直到成功,CAS机制),这种措施不需要挂起线程

从硬件方面保证操作和冲突检测具备原子性(unsafe类)

重量级锁的锁优化技术(Synchronized):

1.自旋锁:某个线程占用了共享数据,本线程先不挂起,而是处于自旋等待状态,不断重试

自旋的次数有限制,不然一直自旋会消耗系统资源

自适应自旋:如果某个锁,自旋很少成功获得,那么以后要获取这个锁时将可能省略掉自旋过程,以避免浪费处理器资源

2.锁消除:指JVM运行时,对一些代码上要求同步,但被检测到不可能存在共享数据竞争的锁进行消除

3.锁粗化:如果一系列的连续操作都对同一个对象反复加锁解锁,甚至是加锁操作出现在循环体中,那即使没有竞争,频繁的进行互斥同步操作也会导致不必要的性能消耗,这个时候将锁的范围扩展,变成一个锁,就是锁的粗化

4.轻量级锁:与Mark Word和CAS机制有关

轻量级锁能提供性能的依据:对于绝大部分,在整个同步周期内是不存在竞争的,这是一个经验数据

Mark Word的组成:

轻量级锁的加锁过程:

1)在代码进入同步块的时候,如果同步对象锁为无锁状态(锁标志位为01状态),虚拟机首先在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储旧的Mark Work的拷贝(锁记录解锁的时候用到)

2)虚拟机使用CAS机制尝试将对象的Mrak Word更新为轻量级锁的标志位和指向锁记录的指针

3)如果更新操作成功,那么线程就拥有了该对象的锁

4)如果这个更新操作失败,虚拟机首先会检查当前线程是否已经有了这个对象的锁,如果已经有了,就进入同步代码块继续执行,如果没有就说明该对象的锁被其他线程占用了,一旦这样,轻量级锁就膨胀成为重量级锁(比如synchronized),Mark Work中存储的就指向重量级锁的指针,后面等待锁的线程也会进入等待状态

轻量级锁的解锁过程:

1).通过CAS机制尝试将当前线程栈帧中的锁记录替换当前的Mark Word

2).如果替换成功,那么整个同步过程就完成了

3).如果替换失败,则说明有其他线程尝试获取过该锁,但失败了,导致轻量级锁变成了重量级锁,那么要在释放锁的同时,唤醒被挂起的线程

总结:轻量级锁就是在无竞争的情况下使用CAS操作区消除同步使用的互斥量

5.偏向锁:在无竞争的情况下,把整个同步过程都消除掉,连CAS操作都不做

偏向锁的依据:锁总是同一个线程持有,很少发生竞争

偏向锁偏向于第一个获得它的线程,如果在接下来的指向过程中,该锁没有被其他线程获取,则持有偏向锁的线程永远不需要进行同步

做法:只需要在锁第一个被拥有的时候,记录下偏向线程ID,这样偏向线程就一直持有着锁,直到竞争发生才释放锁,以后每次同步,检查锁的偏向线程ID是否与当前线程ID一致,如果一致直接进入同步,退出同步也无需每次加锁解锁都去CAS更新Mark Word,如果不一致则意味着发生了竞争,锁已经不总是偏向于一个线程了,这时候锁膨胀为重量级锁才能保证线程公平竞争锁

分析:引入偏向锁是为了在无多线程竞争的情况下尽量减少不必要的轻量级锁执行,因为轻量级锁的释放和获取依赖多次的CAS操作,而偏向锁只需要在置换线程ID的时候依赖一次CAS(由于一旦出现多线程竞争的情况就必须撤销偏向锁,所以偏向锁的撤销操作的性能损耗必须小于节省下来的CAS消耗),轻量级锁是为了在线程交替执行同步块时提高性能,而偏向锁则是在只有一个线程执行同步块时提高性能!

偏向锁加锁过程:

偏向锁加锁发生在偏向线程第一次进入同步块的时候,CAS操作尝试更新对象的Mrak Word(锁标志位为1,记录偏向线程的ID)

撤销偏向锁等待过程:

当有另外一共线程来竞争锁时,就需要将偏向锁膨胀为重量级,竞争线程尝试CAS更新Mark Work失败,会等到安全局点(此时不会执行任何代码)撤销偏向锁

Java线程安全与锁优化的更多相关文章

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

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

  2. Java虚拟机--线程安全和锁优化

    Java虚拟机--线程安全和锁优化 线程安全 线程安全:当多线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象 ...

  3. 深入理解java虚拟机-第13章-线程安全与锁优化

    第十三章 线程安全与锁优化 线程安全 java语言中的线程安全 1 不可变.Immutable 的对象一定是线程安全的 2 绝对线程安全 一个类要达到不管运行时环境如何,调用者都不需要额外的同步措施, ...

  4. 《深入理解Java虚拟机》-----第13章 线程安全与锁优化

    概述 在软件业发展的初期,程序编写都是以算法为核心的,程序员会把数据和过程分别作为独立的部分来考虑,数据代表问题空间中的客体,程序代码则用于处理这些数据,这种思维方式直接站在计算机的角度去抽象问题和解 ...

  5. JVM之java并发 ——线程安全与锁优化

    概述 人们很难想象现实中的对象在一项工作进行期间,会被不停地中断和切换,对象的属性(数据)可能会在中断期间被修改和变“脏”,而这些事情在计算机世界中则是很正常的事情.有时候,良好的设计原则不得不向现实 ...

  6. 深入理解Java虚拟机(第三版)-14. 线程安全与锁优化

    14. 线程安全与锁优化 1. 什么是线程安全? 当多个线程同时访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替进行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个 ...

  7. 《深入了解java虚拟机》高效并发读书笔记——Java内存模型,线程,线程安全 与锁优化

    <深入了解java虚拟机>高效并发读书笔记--Java内存模型,线程,线程安全 与锁优化 本文主要参考<深入了解java虚拟机>高效并发章节 关于锁升级,偏向锁,轻量级锁参考& ...

  8. JVM-并发-线程安全与锁优化

    线程安全与锁优化 1.线程安全 (1)当多个线程访问一个对象时,如果不考虑这些线程在执行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获 ...

  9. jvm(13)-线程安全与锁优化(转)

    0.1)本文部分文字转自“深入理解jvm”, 旨在学习 线程安全与锁优化 的基础知识: 0.2)本文知识对于理解 java并发编程非常有用,个人觉得,所以我总结的很详细: [1]概述 [2]线程安全 ...

随机推荐

  1. Linux 安装 tomcat

    创建目录 cd /usr mkdir tomcat cd tomcat 上传 tomcat rz.ftp 或者 wget 都可以 解压 tar -xzvf apache-tomcat-8.0.53.t ...

  2. Android内存优化(四)LeakCanary使用详解

    LeakCanary是检测App内存泄露的工具, 内存泄露是Android开发中常见的问题, 使用程序的稳定性下降. LeakCanary 的机制如下: RefWatcher.watch() 会以监控 ...

  3. MFC更换画笔(画刷)颜色以及画眼睛(GDI画图)

    MFC画眼睛 换画笔(画刷)颜色(参考链接:https://blog.csdn.net/sunxiving/article/details/51272001) 由于画笔一旦创建后就无法修改.所以要修改 ...

  4. DAY14(PYTHONS)生成器进阶

    def average(): sum = 0 count = 0 avg = 0 while True: #循环 num = yield avg #遇到yield就停止,防止一开始除数为0导致崩溃 s ...

  5. javascript:正则表达式、一个表单验证的例子

    本文内容: 正则表达式 正则表达式的使用方法 正则表达式的特殊匹配字符 正则表达式修饰符 利用正则表达式进行表单验证的例子 首发日期:2018-05-13 正则表达式: 正则表达式的使用方法: 首先创 ...

  6. Spring从认识到细化了解

    目录 Spring的介绍 基本运行环境搭建 IoC 介绍: 示例使用: 使用说明: 使用注意: Bean的实例化方式 Bean的作用范围的配置: 补充: DI: 属性注入: 补充: IoC的注解方式: ...

  7. SQL 中常用存储过程xp_cmdshell运行cmd命令

    目的: 使用SQL语句,在D盘创建一个文件夹myfile 首先查询系统配置 SELECT * FROM sys.configurations WHERE name='xp_cmdshell' OR n ...

  8. jsp笔记----jsp常用的的获取项目的根路径

    <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" ...

  9. 自动化测试基础篇--Selenium中JS处理浏览器弹窗

    摘自https://www.cnblogs.com/sanzangTst/p/7692454.html 浏览器弹窗: 现在大多数网站都会使用自定义弹窗,使用Selenium自带的方法暂时处理不了,这时 ...

  10. 使用Three.js挖空安装门来解决重叠闪烁的问题

    一.挖空原理说明 subtract 用墙面减去与门重叠的部分,产生一个新的对象,导入材质安装门即可 //参与减去几何体 //平行于x轴门 var meshH4Door = new ThreeBSP( ...