深入理解java:2.2. 同步锁Synchronized及其实现原理
同步的基本思想
为了保证共享数据在同一时刻只被一个线程使用,我们有一种很简单的实现思想,就是
在共享数据里保存一个锁 ,当没有线程访问时,锁是空的。
当有第一个线程访问时,就 在锁里保存这个线程的标识 并允许这个线程访问共享数据。
在当前线程释放共享数据之前,如果再有其他线程想要访问共享数据,就要 等待锁释放 。
- 在共享数据里保存一个锁
- 在锁里保存这个线程的标识
- 其他线程访问已加锁共享数据要等待锁释放
Jvm同步的实现
jvm中有以下三种锁(由上到下越来越“重量级”):
- 偏向锁
- 轻量级锁
- 重量级锁
重量级锁
Synchronized 原理
我们直接参考JVM规范中描述:每个对象有一个监视器锁(monitor)。
当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:
1、如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。
2、如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1.
3.如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。
Synchronized的语义底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,
这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。
Synchronized是通过对象内部的一个叫做监视器锁(monitor)来实现的。
但是监视器锁本质又是依赖于底层的操作系统的互斥锁(Mutex Lock)来实现的。而操作系统实现线程之间的切换这就需要从用户态转换到核心态,这个成本非常高,状态之间的转换需要相对比较长的时间,这就是为什么Synchronized效率低的原因。
因此,这种依赖于操作系统互斥锁(Mutex Lock)所实现的锁我们称之为“重量级锁”。
轻量级锁
锁的状态总共有四种:无锁状态、偏向锁、轻量级锁和重量级锁。
JDK 1.6中默认是开启偏向锁和轻量级锁的,我们也可以通过-XX:-UseBiasedLocking来禁用偏向锁。
轻量级锁的核心思想就是“被加锁的代码不会发生并发,如果发生并发,那就膨胀成重量级锁(膨胀指的锁的重量级上升,一旦升级,就不会降级了)”。
轻量级锁依赖了一种叫做CAS(compare and swap)的操作。
术语定义
| 术语 | 英文 | 说明 |
| CAS | Compare and Swap |
比较并设置。 用于在硬件层面上提供原子性操作。 在 Intel 处理器中,比较并交换通过指令cmpxchg实现。比较是否和给定的数值一致,如果一致则修改,不一致则不修改。 |
偏向锁
根据轻量级锁的实现,我们知道虽然轻量级锁不支持“并发”,遇到“并发”就要膨胀为重量级锁,但是轻量级锁可以支持多个线程以串行的方式访问同一个加锁对象。
比如A线程可以先获取对象o的轻量锁,然后A释放了轻量锁,这个时候B线程来获取o的轻量锁,是可以成功获取得,以这种方式可以一直串行下去。
之所以能实现这种串行,是因为有一个释放锁的动作。那么假设有一个加锁的java方法,这个方法在运行的时候其实从始至终只有一个线程在调用,但是每次调用完却也要释放锁,下次调用还要重新获得锁。
那么我们能不能做一个假设:“假设加锁的代码从始至终就只有一个线程在调用,如果发现有多于一个线程调用,再膨胀成轻量级锁也不迟”。这个假设,就是偏向锁的核心思想。
偏向锁依赖了一种叫做CAS(compare and swap)的操作。
总结
本文重点介绍了JDk中采用轻量级锁和偏向锁等对Synchronized的优化,
但是这两种锁也不是完全没缺点的,比如竞争比较激烈的时候,不但无法提升效率,反而会降低效率,因为多了一个锁升级的过程,这个时候就需要通过-XX:-UseBiasedLocking来禁用偏向锁。下面是这几种锁的对比:
|
锁 |
优点 |
缺点 |
适用场景 |
|
偏向锁 |
加锁和解锁不需要额外的消耗,和执行非同步方法比仅存在纳秒级的差距。 |
如果线程间存在锁竞争,会带来额外的锁撤销的消耗。 |
适用于只有一个线程访问同步块场景。 |
|
轻量级锁 |
竞争的线程不会阻塞,提高了程序的响应速度。 |
如果始终得不到锁竞争的线程使用自旋会消耗CPU。 |
追求响应时间。 同步块执行速度非常快。 |
|
重量级锁 |
线程竞争不使用自旋,不会消耗CPU。 |
线程阻塞,响应时间缓慢。 |
追求吞吐量。 同步块执行速度较长。 |
深入理解java:2.2. 同步锁Synchronized及其实现原理的更多相关文章
- Java多线程系列--“JUC锁”10之 CyclicBarrier原理和示例
概要 本章介绍JUC包中的CyclicBarrier锁.内容包括:CyclicBarrier简介CyclicBarrier数据结构CyclicBarrier源码分析(基于JDK1.7.0_40)Cyc ...
- 深入理解Java中的同步静态方法和synchronized(class)代码块的类锁
一.回顾学习内容 在前面几篇博客中我我们已经理解了synchronized对象锁.对象锁的重入.synchronized方法块.synchronized非本对象的代码块, 链接:https://www ...
- 理解java中【同步】和【死锁】
一.理解同步 要想解决资源共享的同步操作问题,可以使用两种方法: 使用同步代码块 之前学习过程中,代码块分为四种: l 普通代码块:是直接定义在方法之中的: l 构造块 ...
- Java多线程的同步机制(synchronized)
一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在 java里边就是拿到某个同步对象的锁(一个对象只有一把锁): 如果这个时候同步对象的锁被其他线程拿走了,他(这个 ...
- java多线程学习-同步(synchronized)
(示例都是网上视频的) 假如两个线程同时调用一个方法输出字符串 public class SynchronizedTest extends Thread { public static void ma ...
- JAVA并发,同步锁性能测试
测试主要从运行时间差来体现,数据量越大,时间差越明显,例子如下: package com.xt.thinks21_2; /** * 同步锁性能测试 * * @author Administrator ...
- 轻松学习java可重入锁(ReentrantLock)的实现原理
转载自https://blog.csdn.net/yanyan19880509/article/details/52345422,(做了一些补充) 前言 相信学过java的人都知道 synchroni ...
- java高并发之锁的使用以及原理浅析
锁像synchronized同步块一样,是一种线程同步机制.让自Java 5开始,java.util.concurrent.locks包提供了另一种方式实现线程同步机制——Lock.那么问题来了既然都 ...
- 轻松学习java可重入锁(ReentrantLock)的实现原理(转 图解)
前言 相信学过java的人都知道 synchronized 这个关键词,也知道它用于控制多线程对并发资源的安全访问,兴许,你还用过Lock相关的功能,但你可能从来没有想过java中的锁底层的机制是怎么 ...
随机推荐
- bash: ipconfig: command not found
问题描述: [root@localhost ~]# ipconfig-bash: ipconfig: command not found[root@localhost ~]# 解决方法一: cd /e ...
- CodeForces-687A(DFS,染色)
链接: https://vjudge.net/problem/CodeForces-687A 题意: Recently, Pari and Arya did some research about N ...
- iOS 自定义NavigationBar右侧按钮rightBarButtonItem
自定义右侧的一个按钮 UIBarButtonItem *myButton = [[UIBarButtonItem alloc] initWithTitle:@"主页" style: ...
- electron-vue 更新 使用electron-update的版本
electron-vue使用的electron版本比较老,用最新的electron-update会报错 我用这个版本成功 "electron-updater": "^3. ...
- linux lazarus 连接mssqlserver
1 . 从https://www.freetds.org/ 下载驱动源文件 2. 参照 https://www.freetds.org/userguide/config.htm 内容编译 3. 启动l ...
- python-套接字编程之udp
使用udp协议 服务端: #!/usr/bin/python3 # coding:utf-8 # Auther:AlphaPanda # Description:UDP服务端 # Version:1 ...
- oracle 7.4安装nvidia驱动
2019-8-28 参考网页: 如何在k8s集群中安装nvidia.cuda并使用GPU进行训练 https://blog.csdn.net/u013042928/article/details/78 ...
- AJAX - 服务器 响应
AJAX - 服务器 响应 服务器响应 如需获得来自服务器的响应,请使用 XMLHttpRequest 对象的 responseText 或 responseXML 属性.大理石构件来图加工 属性 描 ...
- Bootstap
Bootstrap框架 Bootstrap框架 Bootstrap介绍 Bootstrap是Twitter开源的基于HTML.CSS.JavaScript的前端框架. 它是为实现快速开发Web应用程序 ...
- 02-scrapy的cmdline命令
1.启动爬虫的命令为: scrapy crawl spidername(爬虫名) 2.我们还可以通过下述方式来启动爬虫 步骤一:创建一个.py文件.startspider.py(这里文件的名称可以自己 ...