java 并发——synchronized
java 并发——synchronized
介绍
在平常我们开发的过程中可能会遇到线程安全性的问题,为了保证线程之间操作数据的正确性,我们第一想到的可能就是使用 synchronized 并且 synchronized 使用的位置也是很有讲究的.首先我们来先看一下什么是 synchronized ?
- 需要使得代码变为同步方法我们需要使用 synchronized 来修饰执行前会先去获取锁,执行完释放锁,执行期间其他线程会等待.
- synchronized 有使用方法修饰在方法前面和修饰代码块.
- synchronized 以性能为代价来保证数据的完整性,所以非必要勿用.
- synchronized 只适用于单 jvm 虚拟机内,如果项目是集群的那么 synchronized 将不能保证数据是安全的.
使用场景说明
如果 synchronized 修饰在实例方法上,那么当前的锁对象就是该类的实例对象.
public synchronized void test() {
//...
}
如果 synchronized 修饰在静态方法上,那么当前的锁对象就是类对象.
public static synchronized void test() {
//...
}
如果 synchronized 修饰同步代码块中是 this 那么当前锁对象还是类的实例对象.
public void test() {
synchronized(this) {
//...
}
}
如果 synchronized 修饰同步代码块中是 Class 那么当前锁对象就是类对象.
public void test() {
synchronized(synchronizedDemo.class) {
//...
}
}
如果 synchronized 修饰同步代码块中是任意一个对象那么当前锁对象就是这个对象实例.
private final Object lock = new Object(); public void test() {
synchronized(lock) {
//...
}
}
是如何实现的呢?
我们先来先写一个例子
public class SynchronizedTest {
public synchronized void test01() {
System.out.println("test01");
}
public void test02() {
synchronized (this) {
System.out.println("test02");
}
}
}
之后我们先通过 javac 来编译成 class 文件之后再 javap 查看 class 文件发现如下图
[外链图片转存失败(img-Ilzk5vPR-1567669713818)(D:\Typora\image\1567666073594.png)]
我们通过上图发现 test01 这个方法给打上了一个标识 ACC_SYNCHRONIZED 来实现的。
而我们的 test02 同步代码块使用了 monitorenter 指令插入到了代码块开始的位置,monitorexit 指令插入到代码块结束的位置。必须是成对出现的.
在上面我们对 synchronized 已经有了一个大致的认识了,但是我们继续想学习锁的知识,就必须涉及到 cas 操作和 java 对象头了.
cas 操作
cas: compare and swap.
百度百科: cas 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该位置的值。(在 CAS 的一些特殊情况下将仅返回 CAS 是否成功,而不提取当前值。)CAS 有效地说明了“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。
简单一点来说就是 cas 有 3 个操作数,内存值 V,旧的预期值 A,要修改的新值 B。如果 A = V,那么把 B 赋值给 V,返回 V;如果 A != V,直接返回 V。所以为了提高性能 jvm 很多操作都是依赖 cas 来实现的,cas 也就是乐观锁的实现.
java 对象头
java 对象头包含两个部分
|--------------------------------------------------------------|
| Object Header (64 bits) |
|------------------------------------|-------------------------|
| Mark Word (32 bits) | Klass Word (32 bits) |
|------------------------------------|-------------------------|
- Mark World: 主要用于存储自身运行时的数据哈希码、GC 分代年龄、锁状态标志、线程持有的锁、偏向线程 id、偏向时间戳等等.
- Klass Pointer: 类型指针.指向它的类元数据的指针,虚拟机通过这个指针来确定对象是哪个类的实例.
对象头中的 Mark Word,synchronized 实现就用了 Mark Word 来标识对象加锁状态.下面是 32 位虚拟机头的存储结构

看了上图我们发现有好几种锁偏向锁、轻量级锁、重量级锁(synchronized)其实是 jdk1.6 中对锁的实现引入了大量的优化来减少锁操作的开销:
首先我们来看下锁的枚举下面图中会看到: 00 偏向锁、01 无锁、10 监视器锁,又叫重量级锁、11 GC标记、101 偏向锁.
锁粗化: 将多个连续的锁扩展成一个大范围的锁,用来减少频繁的互斥获取锁释放锁导致的性能消耗.
锁消除: jvm 通过检测判断一段代码中不可能存在共享数据竞争,jvm 会对这些同步锁进行锁消除。锁消除的依据是逃逸分析的数据支持。
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
} public void test() {
StringBuffer sb = new StringBuffer();
sb.append("");
}
我们都知道 StringBuffer 是线程安全的,我们虽然没有显示使用锁,但是我们在使用一些 jdk 的内置 API 时,如StringBuffer、Vector、HashTable 等,这个时候会存在隐形的加锁操作。
轻量级锁: 在没有多线程竞争的情况下避免重量级互斥锁,只需要依靠一条 cas 原子指令就可以完成锁的获取及释放.

偏向锁: 目的是消除数据再无竞争情况下的同步。使用 cas 记录获取它的线程。下一次同一个线程进入则偏向该线程,无需任何同步操作.

自旋锁: 就是让该线程等待一段时间,不会被立即挂起,看持有锁的线程是否会很快释放锁。怎么等待呢?执行一段无意义的循环即可(自旋).
适应自旋锁: jdk1.6 引入了更加聪明的自旋锁,即自适应自旋锁。所谓自适应就意味着自旋的次数不再是固定的,它是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。它怎么做呢?线程如果自旋成功了,那么下次自旋的次数会更加多,因为虚拟机认为既然上次成功了,那么此次自旋也很有可能会再次成功,那么它就会允许自旋等待持续的次数更多。反之,如果对于某个锁,很少有自旋能够成功的,那么在以后要或者这个锁的时候自旋的次数会减少甚至省略掉自旋过程,以免浪费处理器资源。
重量级锁
经过 jdk 的优化所以加锁流程是:偏向锁——>轻量级锁——>重量级锁 并且锁只能升级膨胀并不能降级.
总结
这次主要说了 synchronized 原理以及 jdk 对 synchronized 的优化。简单来说解决三种场景:
1)只有一个线程进入临界区,偏向锁.
2)多个线程交替进入临界区,轻量级锁.
3)多线程同时进入临界区,重量级锁.
就好比是你在周末一个人在公司公司突然肚子痛要上厕所,这个时候不需要等待也随便你关不关厕所门就可以上厕所(偏向锁)哈哈哈.但是在工作日的话你去上厕所发现测试门被关闭了这个时候你选择站在门口等待一会(自旋锁).之后你等待了 5 分钟后发现没什么希望,你就回到座位去等了(锁膨胀重量级锁).例子可能举的不是那么明确,但是也只能想到这样了.感谢观看!
java 并发——synchronized的更多相关文章
- Java并发——synchronized关键字
前言: 只要涉及到Java并发那么我们就会考虑线程安全,实际上能够实现线程安全的方法很多,今天先介绍一下synchronized关键字,主要从使用,原理介绍 一.synchronized的使用方法 1 ...
- Java并发-Synchronized关键字
一.多线程下的i++操作的并发问题 package passtra; public class SynchronizedDemo implements Runnable{ private static ...
- Java并发--synchronized
以下是本文的目录大纲: 一.什么时候会出现线程安全问题? 二.如何解决线程安全问题? 三.synchronized同步方法或者同步块 转载原文链接:http://www.cnblogs.com/dol ...
- Java并发synchronized详解
今天和大家一起学习下并发编程,先举一个简单的生活例子,我们去医院或者银行排队叫号,那每个工作人员之间如何保证不会叫重号呢? public class TicketDemo extends Thread ...
- 精通java并发-synchronized关键字和锁
目前CSDN,博客园,简书同步发表中,更多精彩欢迎访问我的gitee pages synchronized关键字和锁 示例代码 public class MyThreadTest2 { public ...
- 深入理解Java并发synchronized同步化的代码块不是this对象时的操作
本文仅仅是为了说明synchronized关键字同步的是对象不是方法,列子的确有失偏颇. 一.明确一点synchronized同步的是对象不是方法也不是代码块 我有关synchronized同步的是 ...
- java 并发synchronized使用
从版本1.0开始,java中每个对象都有一个内部锁,如果一个方法用synchronized修饰,那么对象的锁将保护整个方法,也就是说要调用该方法,线程必须获得内部的对象锁 换句话说 public sy ...
- Java并发——synchronized和ReentrantLock的联系与区别
0 前言 本文通过使用synchronized以及Lock分别完成"生产消费场景",再引出两种锁机制的关系和区别,以及一些关于锁的知识点. 本文原创,转载请注明出处:http:// ...
- Java并发—synchronized关键字
synchronized关键字的作用是线程同步,而线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏. synchronized用法 1. 在需要同步的方法的方法签名中加入synchro ...
随机推荐
- git使用记录四:.git分析
git使用记录四: .git 查看.git 目录下的文件 soaeon@DESKTOP-FUJJTHR MINGW64 /f/gitstudy/.git (GIT_DIR!) $ ls -al tot ...
- vue中修改了数据但视图无法更新的情况(转)
原文地址:https://blog.csdn.net/qq_39985511/article/details/79778806
- md5加密 和拉钩网的登录
#使用requests模块 #1.登录lagou #2.登录人人,保存个人首页 import requests from urllib import parse #hashlib是MD5加密的一个py ...
- 使用UI Automation实现自动化测试--5-7
使用UI Automation实现自动化测试--5 (Winfrom和WPF中弹出和关闭对话框的不同处理方式) 在使用UI Automation对Winform和WPF的程序测试中发现有一些不同的地方 ...
- Spring clound 微服务--理解篇
定义:微服务就是一些协调工作的小而自治的服务 优点: 异构性:不同微服务可以使用不同的语言实现, 后端数据库也可以根据自身业务定义服务. 弹性: 一个组件不可用,不会导致级联故障.一个系统出了问题,不 ...
- Windows平台下在Emacs中使用plantuml中文乱码问题(已解决)
Windows平台下在Emacs中使用plantuml中文乱码问题(已解决) */--> code {color: #FF0000} pre.src {background-color: #00 ...
- 后端数据推送-EventSource
服务器发送事件(以下简称SSE)是HTML 5规范的一个组成部分,可以实现服务器到客户端的单向数据通信.通过SSE,客户端可以自动获取数据更新,而不用重复发送HTTP请求.一旦连接建立,“事件”便会自 ...
- django中动态生成二级菜单
一.动态显示二级菜单 1.修改权限表结构 (1)分析需求,要求左侧菜单如下显示: 客户管理: 客户列表 账单管理: 账单列表 (2)修改rbac下的models.py,修改后代码如下: from dj ...
- python5数据存储
1 txt文件存储 正常调用文件python文件操作 https://www.cnblogs.com/x2x3/p/9979919.html 2 json文件存储 在JavaScript语言中,一切都 ...
- PHP数组函数实现栈与队列的方法介绍(代码示例)
根据php提供的四个关于数组的函数: array_push(),array_pop(),array_unshift(),array_shift() 配合数组本身,一下子就实现了栈(stack)和队例( ...