对synchronized的一点理解
一、synchronized的使用
(一)、synchronized同步方法
1. “非线程安全”问题存在于“实例变量”中,如果是方法内部的私有变量,则不存在“非线程安全”问题。
2. 如果多个线程共同访问1个对象中的实例变量,则有可能出现“非线程安全”问题。
3. synchronized取得的锁都是对象锁,而不是把一段代码或方法当做锁。因此在多线程访问同一个对象时,哪个线程先执行synchronized关键字的方法,哪个线程就持有该方法所属对象的锁lock,其他线程只能呈等待状态。
但如果多个线程访问多个对象,则JVM会创建多个锁。
4. 调用关键字synchronized声明的方法一定是排队运行的。另外只有共享资源的读写访问才需要同步化,如果不是共享资源,就没有同步化的必要。
5. 脏读。当A线程调用anyObject对象加入synchronized关键字的X方法时,A线程就获得了X方法锁,更准确地讲,是获得了对象的锁,所以其他线程必须等A线程执行完毕才可以调用X方法,但B线程可以随意调用其他的非synchronized关键字的同步方法。
6. synchronized锁重入。使用synchronized时,当一个线程得到一个对象锁后,再次请求此对象锁时,是可以再次得到该对象的锁的。
7. 当一个线程执行的代码出现异常时,其所持有的锁会自动释放。
8. 同步不可以继承。如果父类方法使用了关键字synchronized,那么子类方法不会继承synchronized,如果子类需要同步,还需要在子类方法中添加synchronized。
(二)、synchronized同步代码块
1. 同一个方法中,不在synchronized块中的就是异步执行,在synchronized块中的就是同步执行。
2. 当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对同一个object中所有其他synchronized(this)同步代码块的访问将被阻塞,这是因为synchronized使用的是“对象监视器”。
3. 和synchronized方法一样,synchronized(this)代码块也是锁定当前对象。
4. java还支持对“任意对象”作为“对象监视器”来实现同步的功能。这个任意对象大多数是实例变量及方法的参数,使用格式为:synchronized(非this对象x),此时将x对象本身作为“对象监视器”
锁非this对象的优点:如果在一个类中有很多个synchronized方法,这时虽然能实现同步,但会受到阻塞,所以影响运行效率;但如果使用同步代码块锁非this对象,则synchronized(非this对象x)代码块中的程序与同步方法是异步的,不与其他锁this同步方法争抢this锁,可以大大提高运行效率。
5. synchronized还可以应用在static静态方法上,如果这样写,就是对当前的*.java文件对应的class类进行加锁。
跟synchronized加到非static方法上的区别:synchronized加到static静态方法上,是给Class类上锁;而synchronized加到非static静态方法上,是给对象上锁。
6. 由于JVM具有String常量池缓存的功能,因此在大多数情况下,同步synchronized代码块都不使用String作为锁对象:synchronized(string),而改用其他的,比如new Object实例化一个Object对象,但它并不放入缓存中。
以上方法的具体代码例子可以参考《java多线程编程核心技术》
二、synchronized的实现原理
目前在Java中存在两种锁机制:synchronized和Lock
synchronized实现同步的基础:Java中每一个对象都可以作为锁,具体表现为以下3种方式:(Synchronized锁, 锁住的是对象。)
1. 对于普通同步方法,锁是当前实例对象
2. 对于静态同步方法,锁是当前类的class对象
3. 对于同步方法块,锁是synchronized括号里配置的对象
synchronized用的锁是存在java对象头里的。重量级锁、轻量级锁和偏向锁之间的转换:

Synchronized同步块和同步方法在实现上的区别:
在下面的例子中,使用了同步块和同步方法,通过使用javap工具查看生成的class文件信息,来分析synchronized关键字的实现细节。
public class Synchronized {
public static void main(String[] args) {
// 对Synchronized Class对象进行加锁
synchronized (Synchronized.class) {
}
// 静态同步方法,对Synchronized Class对象进行加锁
m();
}
public static synchronized void m() {
}
}
在synchronized.class的同级目录下执行:javap -v synchronized.class,部分输出如下:

说明:
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC //方法修饰符,表示public static
Code:
stack=2, locals=1, args_size=1
0: ldc #1 // class testnew/Synchronized
2: dup
3: monitorenter // monitorenter:监视器进入,获取锁
4: monitorexit // monitorexit: 监视器退出,释放锁
5: invokestatic #16 // Method m:()V
8: return public static synchronized void m();
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED //方法修饰符,表示public static synchronized
Code:
stack=0, locals=0, args_size=0
0: return
对于同步块的实现使用了monitorenter和monitorexit指令,而同步方法则是依靠方法修饰符上的ACC_SYNCHRONIZED来完成的。无论采用哪种方法,其本质是对一个对象的监视器(monitor)进行获取,而这个获取过程是排他的,也就是同一个时刻只能有一个线程获取到由synchronized所保护对象的监视器。
任意一个对象都拥有自己的监视器,当这个对象由同步块或者这个对象的同步方法调用时,执行方法的线程必须先获取到该对象的监视器才能进入同步块或者同步方法,而没有获取到监视器(执行该方法)的线程将会被阻塞在同步块和同步方法的入口处,进入BLOCKED状态。
下图描述了对象、对象的监视器、同步队列和执行线程之间的关系。

可以看出,任意线程对Object(Object由synchronized保护)的方法,首先要获得Object的监视器。如果获取失败,线程进入同步队列,线程状态变为BLOCKED。当访问Object的前驱(获得了锁的线程)释放了锁,则该释放操作唤醒阻塞在同步队列总的线程,使其重新尝试对监视器的获取。
相关阅读:java中的锁分类
参考:
《Java并发编程的艺术》
Java中的锁机制 synchronized & 偏向锁 & 轻量级锁 & 重量级锁 & 各自优缺点及场景 & AtomicReference
对synchronized的一点理解的更多相关文章
- java 多线程 Synchronized方法和方法块 synchronized(this)和synchronized(object)的理解
synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块. 1. synchronized 方法:通过在方法声明中加入 synchronized ...
- opencv笔记5:频域和空域的一点理解
time:2015年10月06日 星期二 12时14分51秒 # opencv笔记5:频域和空域的一点理解 空间域和频率域 傅立叶变换是f(t)乘以正弦项的展开,正弦项的频率由u(其实是miu)的值决 ...
- 对socket的一点理解笔记
需要学web service,但是在视频中讲解到了socket套接字编程.以前貌似课上老师有提过,只是没用到也感觉乏味.现在遇到,自己看了些博客和资料.记录一点理解,不知正确与否. 首先说这个名字,叫 ...
- iOS 的一点理解(一) 代理delegate
做了一年的iOS,想记录自己对知识点的一点理解. 第一篇,想记录一下iOS中delegate(委托,也有人称作代理)的理解吧. 故名思议,delegate就是代理的含义, 一件事情自己不方便做,然后交 ...
- 关于web开发的一点理解
对于web开发上的一点理解 1 宏观上的一点理解 网页从请求第地址 到获得页面的过程:从客户端(浏览器)通过地址 从soket把请求报文封装发往服务端 服务端通过解析报文并处理报文最后把处理的结果 ...
- angular.js的一点理解
对angular.js的一点理解 2015-01-14 13:18 by MrGeorgeZhao, 317 阅读, 4 评论, 收藏, 编辑 最近一直在学习angular.js.不得不说和jquer ...
- RxSwift 入坑好多天 - 终于有了一点理解
一.前言 江湖上都在说现在就要赶紧学 swift 了,即将是 swift 的天下了.在 api 变化不大的情况下,swift 作为一门新的语言,集众家之所长,普通编码确实比 oc 要好用的多了 老早就 ...
- rt-thread中动态内存分配之小内存管理模块方法的一点理解
@2019-01-18 [小记] rt-thread中动态内存分配之小内存管理模块方法的一点理解 > 内存初始化后的布局示意 lfree指向内存空闲区首地址 /** * @ingroup Sys ...
- rt-thread中软件定时器组件超时界限的一点理解
@2019-01-15 [小记] 对 rt-thread 中的软件定时器组件中超时界限的一点理解 rt_thread_timer_entry(void *parameter)函数中if ((next_ ...
随机推荐
- post提交方式
post提交方式 为提交 url 路径后的name值 getParameter 是获取url后面的参数的.getattribute 是获取 自己setattribute的.
- css学习杂记
1.css中的&语法 &是sass的语法,代表上一级选择器. 例如: .el-row { margin-bottom: 20px; &:last-child { margin- ...
- Netty源码分析第4章(pipeline)---->第7节: 前章节内容回顾
Netty源码分析第四章: pipeline 第七节: 前章节内容回顾 我们在第一章和第三章中, 遗留了很多有关事件传输的相关逻辑, 这里带大家一一回顾 首先看两个问题: 1.在客户端接入的时候, N ...
- 我对BP网络的简单的理解
最近在学习tf的神经网络算法,十多年没有学习过数学了,本来高中数学的基础,已经彻底还给数学老师了.所以我把各种函数.公式和推导当做黑盒子来用,理解他们能做到什么效果,至于他们是如何做到的,暂时不去深究 ...
- PHP.ini 能不能加载子配置文件 ?
答案是不能,php这个地方用的是另一个方案解决的 编译的时候 用这个参数 --with-config-file-scan-dir指定一个目录 然后在这个目录里面加载ini https://www. ...
- 使用vbox构建局域网络
update: 也可以启用DHCP自动分配IP地址.(看到过的某一篇博文写过要使用这个服务还得自己搭--就没有动手去实践一下直接手动分配了静态的IP.偶然尝试了一下发现动态IP分配和手动静态IP分配都 ...
- centos安装eclise启动报错
A Java Runtime Environment (JRE) or Java Development Kit (JDK) must be avail http://blog.csdn.net/u0 ...
- 20135332 第一次JAVA实验报告
课程:Java程序设计 班级: 1353 姓名:武西垚 学号:20135332 成绩: 指导教师:娄嘉鹏 实验日期:2 ...
- 20172322 实验一《Java开发环境的熟悉》实验报告
172322 2017-2018-2 <程序设计与数据结构>实验一报告 课程:<程序设计与数据结构> 班级: 1723 姓名: 张昊然 学号:20172322 实验教师:王志强 ...
- 第五周作业总结(内含用Junit测试ArrayStack和LinkedStack课堂练习报告)
---恢复内容开始--- 学号 20162310<程序设计与数据结构>第五周学习总结 教材学习内容总结 集合分为线性集合(集合中的元素排成一行)和非线性集合(按不同于一行的方式来组织元素, ...