一、Java中锁的概念

  • 自旋锁:是指当一个线程获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能被成功获取,直到获取到锁才会退出循环。
  • 乐观锁:假定没有冲突,在修改数据时如果发现数据和之前获取的不一致,则读最新数据,重试修改。
  • 悲观锁:假定会发生并发冲突,同步所有对数据的相关操作,从读数据就开始上锁。
  • 独享锁(写):给资源加上写锁,线程可以修改资源,其它线程不能再加锁(单写)。
  • 共享锁(读):给资源加上读锁后只能读不能修改,其它线程也只能加读锁,不能加写锁(多度)。看成Semaphore(信号量)理解即可。
  • 可重入锁&不可重入锁:线程拿到一把锁之后,可以自由进入同一把锁所同步的其它代码。
  • 公平锁&非公平锁:争抢锁的顺序,如果是按先来后到,则为公平。即能保证抢锁的顺序和抢到锁的顺序一致则为公平锁。

二、同步关键字synchronized特性

特性:可重入、独享、悲观锁。

锁相关的优化:

  • 锁消除 :开启锁消除的参数有 -XX: DoEscapeAnalysis-XX: EliminateLocks
  • 锁粗化:JDK做了锁粗化的优化,但我们自己可从代码层面优化。

1、锁消除示例

/**
 * 锁消除示例,JIT即时编译,进行了锁消除
 * @author 刘亚楼
 * @date 2020/1/16
 */
public class LockEliminationExample {
	/**
	 * StringBuilder线程不安全,StringBuffer用了synchronized关键字,是线程安全的
	 * 针对下面这种单线程加锁、解锁操作,JIT会进行优化,进行锁消除
	 */
	public static void eliminateLock() {
		StringBuffer stringBuffer = new StringBuffer();
		stringBuffer.append("a");
		stringBuffer.append("b");
		stringBuffer.append("c");
		stringBuffer.append("a");
		stringBuffer.append("b");
		stringBuffer.append("c");
		stringBuffer.append("a");
		stringBuffer.append("b");
		stringBuffer.append("c");
	}
}

2、锁粗化示例

/**
 * 锁粗化示例
 * @author 刘亚楼
 * @date 2020/1/16
 */
public class LockCoarseningExample {
	/**
	 * 针对下面这种无意义的加锁操作,JIT会进行优化,对变量i的所有操作放到一个同步代码块里
	 */
	public static void lockCoarsening() {
		int i = 0;
		synchronized (LockCoarseningExample.class) {
			i  ;
		}
		synchronized (LockCoarseningExample.class) {
			i--;
		}
		synchronized (LockCoarseningExample.class) {
			i  ;
		}
		synchronized (LockCoarseningExample.class) {
			i  ;
			i--;
			i  ;
		}
	}
}

备注:锁消除和锁粗化的区别在于锁消除是针对单个线程重复加解锁做的优化,最终没有锁的存在。而锁粗化不只是针对单线程,且最终还是有锁的存在。

三、synchronized关键字原理

1、关于Mark Word

首先,对象在堆中由对象头、实例数据和对齐填充组成。

对象头包含两部分信息,第一部分用于存储对象自身的运行时数据,如哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向锁id等,这部分数据官方称为"Mark Word"。

对象头的另一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。

synchronized实现的锁是通过改变对象头的"Mark Word"来实现的。

"Mard Word"在32位和64位的虚拟机(未开启压缩指针)中分别为32位和64位。32位虚拟机"Mark Word"如下:

2、锁的状态变化

(1) 无锁 → 轻量级锁

无锁变成轻量级锁时,多个线程会读取对象的对象头的无锁状态mark word内容,然后进行cas操作进行修改,预期值是无锁状态mark word内容,新值是轻量级锁状态mark word内容,若修改成功,Lock record address指向成功获取锁的线程的Lock Record

演示流程如下:

(2) 轻量级锁 → 重量级锁

由于未成功获取锁的线程会自旋,长时间自旋会消耗CPU资源,因此自旋到一定次数会进行锁升级,由轻量级锁转变为重量级锁。

重量级锁是通过object monitor(对象监视器)实现的,对象监视器包括entryList(锁池)、owner(持锁者)、waitSet(等待集合)等。

升级为重量级锁时对象头mark word的内容是monitor address(对象监视器地址),指向对象监视器。

演示流程如下:

备注:抢锁失败线程会进入entryList(锁池),在调用wait方法后,线程会进入waitSet(等待集合),waitSet中的线程被唤醒后会重新进入entryList。

(3) 关于偏向锁

加锁之后不解锁,针对单线程

所谓偏向就是偏心,单线程加锁之后就不再解锁,减少了加锁→业务处理→释放锁→加锁操作流程。

在JDK6以后,默认已经开启了偏向锁这个优化,通过JVM参数-XX:-UseBiasedLocking来禁用偏向锁,若偏向锁开启,只有一个线程抢锁,可获取到偏向锁。

关于偏向锁Mark Word内容如下:

偏向标记第一次有用,出现过争用后就没用了。

偏向锁本质就是无锁,如果没有发生过任何多线程争抢锁的情况,JVM认为就是单线程,无需做同步。

备注:JVM为了少干活,同步在JVM底层是有很多操作来实现的,如果没有争用,就不需要去做同步操作。

(4) 完整的锁升级过程

如果未开启偏向锁,无锁状态会先升级为轻量级锁,轻量级锁自选到一定程度升级为重量级锁。

如果开启了偏向锁,有两种情况:

  • 当锁未被占用时,会升级为无锁,无锁再升级为轻量级锁,再由轻量级锁升级为重量级锁。
  • 当锁被占用时,会升级为轻量级锁,再由轻量级锁升级到重量级锁。

到此这篇关于Java关键字synchronized原理与锁的状态详解的文章就介绍到这了,更多相关Java synchronized内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

Java关键字synchronized原理与锁的状态详解的更多相关文章

  1. Swift线程安全详解-概念,三种锁,死锁,Atomic,synchronized

    研究了下,是线程安全问题。UIKit以及Fundation事实上,大多数Cocoa提供的Api都不是线程安全的,尤其是与UI相关的UIKit,只能在主线程上操作。需要线程安全的时候,开发者自己维护就可以了。用来加锁,解锁。关于ObjectiveC参考这篇文章Atomic一个非Atomic的属性在非ARC的时候像这样可以看到,如果在多线程同时set的情况下,可能会造成release两次。Property的Runtime对应的C代码为可以看到,如果是nonatomic的,synchronized可以看看这个S

  2. 【swift Objective-c】深入了解 核心比较语言特性

    ###OC与swiftautoreleasepoolsynchronizedappledoc关于线程安全objc_sync源码

  3. 并发 – 什么是Swift相当于Objective-C的“@synchronized”?

    我搜索了Swift的书,但找不到Swift版本的@synchronized。如何在Swift中实现互斥?它比@synchronized有点更冗长,但作为一个替代品工作得很好:

  4. android – 指令’monitor-enter v1’时出现空指针异常

    堆栈跟踪:解决方法如果localDBUtility为null,则无法对其进行同步调用.您只能在实例化对象上进行同步.

  5. android – 如何通过MediaRecorder.start()来静音“嘟嘟”?

    解决方法虽然我来不及回答它.它仍然可以帮助所有人都在谷歌搜索同样的问题.在开始媒体记录器之前添加以下两行代码..它会静音手机声音..启动录音机后等待一到两秒钟并取消静音,你可以使用以下可运行的…

  6. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  7. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  8. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  9. Java实现世界上最快的排序算法Timsort的示例代码

    Timsort 是一个混合、稳定的排序算法,简单来说就是归并排序和二分插入排序算法的混合体,号称世界上最好的排序算法。本文将详解Timsort算法是定义与实现,需要的可以参考一下

  10. Java日期工具类的封装详解

    在日常的开发中,我们难免会对日期格式化,对日期进行计算,对日期进行校验,为了避免重复写这些琐碎的逻辑,我这里封装了一个日期工具类,方便以后使用,直接复制代码到项目中即可使用,需要的可以参考一下

随机推荐

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  4. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  7. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  8. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  9. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部