深入理解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中的锁底层的机制是怎么 ...
随机推荐
- PHP对有道翻译API函数
首先需要先到http://fanyi.youdao.com/openapi?path=data-mode申请APIKEY.然后公布代码: <?php header("Content-T ...
- 购物网站被p.egou.com强制恶意劫持
今天早上打开电脑浏览京东,发现随便点击商品,都自动转化为淘客推广的页面, 我以为是360浏览器自己干的,然后我换了谷歌,也是一样,难道这是电脑里面有流氓插件? 我又换了火狐,还是一样,没办法了,换IE ...
- Python中最常见括号()、[]、{}的区别
在Python语言中最常见的括号有三种,分别是:小括号().中括号[].花括号{}:其作用也不相同,分别用来代表不同的Python基本内置数据类型. Python中的小括号(): 代表tuple元祖数 ...
- 013:URL传参数
URL传参数有两种方式: 1.采用在URL中使用变量的方式:在path的第一个参数中,使用'<参数名>'的方式可以传递参数,然后在对于的视图函数中也要写一个参数,并且视图函数中的参数名和U ...
- vue 搜索关键词 变颜色
<a class="text"> <span>{{item.name.slice(0,item.name.toLowerCase().indexOf(inp ...
- Git远程新建分支之后,本地看不到最新分支
git remote update origin –prune 注意 双- csdn 对于 两个 - 看起来像一个
- js 数组 splice 函数 多线程
<script type="text/javascript"> var arr = new Array(6) arr[0] = "00" arr[1 ...
- ex1.c 补
表达式和符号运用生疏 经常少符号
- python3学习笔记(三):注释和字符串
一.注释 为了让别人能够更容易理解程序,使用注释是非常有效的,即使是自己回头再看旧代码也是一样. # 打印圆的周长: print(2* pi* r) 在python 中用井号(#)表示注释.井号(#) ...
- cmd命令行的FTP使用
进入ftp:ftp 打开连接:open 192.168.1.106 2121 用户名空:none 密码空:不用输入,直接回车 查询远程服务器当前路径:pwd 显示远程服务器当前路径下的文件:dir 远 ...