线程安全的严谨定义:

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

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. Mysql 用户和权限

    创建用户 CREATE USER '用户名'@'localhost' IDENTIFIED BY '密码'; 删除用户 DROP USER '用户名'@'localhost'; 权限列表 ALL 或 ...

  2. JAVA代码根据经纬度范围计算WGS84与谷歌全球墨卡托包含的切片数目与拼接图像像素尺寸

    根据项目需求编写的代码. 适用场景:在网络地图上,比如天地图与谷歌地图,用户用鼠标在地图上拉一个矩形框,希望下载该矩形框内某一层级的瓦片数据,并将所有瓦片拼接成一个完整的,包含地理坐标的tif图像. ...

  3. Android 9.0更新

    北京时间2018年8月7日上午,Google 发布了 Android 9.0 操作系统.并宣布系统版本 Android P 被正式命名为代号"Pie". Android 9.0 利 ...

  4. git 入门教程之本地和远程仓库的本质

    本地仓库和远程仓库在本质上没有太大区别,只不过一个是本地电脑,一个是远程电脑. 远程仓库不一定非得是 github 那种专门的"中央服务器",甚至局域网的另外一台电脑也可以充当&q ...

  5. 云ERP真的靠谱吗?

    现在几乎每个IT系统或项目都要跟云挂上钩,跟数码产品必与“智能”扯上关系一样,否则在外行甚至同行眼里就是“矮小搓”.ERP领域也悄然刮起了云端化.国内ERP产品也借此机会想弯道超车,通过云化来抢夺被S ...

  6. Spark性能优化【Stack Overflow】

    一.异常情况 Stack Overflow 二.异常分析 之所以会产生Stack Overflow,原因是在Stack方法栈中方法的调用链条太长的原因导致的,一般情况有两种: 1.过于深度的递归[常见 ...

  7. Table 'performance_schema.session_variables' doesn't exist错误的一

    mysql驱动 jar 换到现在最新的mysql-connector-java-5.1.39-bin.jar

  8. SQL2008无法附加数据库,提示“无法显示请求的对话框”(nColIndex实际值是-1)图文解决方法

    SQL2008无法附加数据库,提示“无法显示请求的对话框”(nColIndex实际值是-1)图文解决方法 SQL2008无法附加数据库,提示“无法显示请求的对话框”(nColIndex实际值是-1)图 ...

  9. MySQL【Delete误操作】数据恢复【转】

    前言:      操作数据库时候难免会因为“大意”而误操作,需要快速恢复的话通过备份来恢复是不太可能的,因为需要还原和binlog差来恢复,等不了,很费时.这里先说明下因为Delete 操作的恢复方法 ...

  10. 解决PowerDesigner不同表输入相同字段名被禁止问题

    选择"Tools -> Model Options"后,弹出如下图所示窗口: 这两个选项取消勾选