对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_ ...
随机推荐
- POM.XML文档汉化
#class_issueManagement project 所述 <project> 元素是描述符的根.下表列出了所有可能的子元素. modelVersion:String:声明此POM ...
- mongodb主从以及auth主从 配置详解
1.主服务器配置 #auth = true dbpath = /data/mongodb/db logpath = /data/mongodb/log/mongodb.log port = ...
- Educational Codeforces Round 61 (Rated for Div. 2) E. Knapsack
非常经典的dp题,因为1至8的最大公约数是840,任何一个数的和中840的倍数都是可以放在一起算的, 所以我只需要统计840*8的值(每个数字(1-8)的sum%840的总和),剩下都是840的倍数 ...
- centos7上的postgresql10安装和配置
安装数据库 安装参考官方文档:https://www.postgresql.org/download/linux/redhat/ 1.Install the repository RPM: yum i ...
- NO---20 文件上传
文件上传是我们会经常用到的一个业务,其实在h5中新增了FormData的对象,用它来提交表单,并且可以提交二进制文件,所以今天就写写文件上传,希望可以对大家有帮助 FormData 上传文件实例 首先 ...
- 多表查询sql语句
多表查询sql语句 1 --解锁SCOTT用户 2 alter user scott account unlock 3 --检索指定的列 4 select job,ename,empno from e ...
- 从一个简单的寻路问题深入Q-learning
这第一篇随笔实际上在我的科学网博客上是首发,我重新拿到博客园再发一次是希望以此作为我学习Q-learning的一个新的开始.以后这边主技术,科学网博客主理论.我也会将科学网那边技术类的文章转过来的.希 ...
- 感谢Thunder
感谢Thunder团队中的每一位成员. 组长王航认真负责,是一个合格优秀的领导者与伙伴,老师布置的任务都会及时分配给每个人,对待每一项任务都认真严谨负责,了解每个成员的优势及强项. 成员李传康.宋雨. ...
- “学霸系统”之NABC
我们团队这次选择的是“学霸系统”客户端项目: 1.需求(need) 作为一款和网上教学问答系统具有相似功能的手机客户端,具体的功能已给出要求:用户管理.搜索.分类.上传下载.用户贡献与交互等功能. ( ...
- Linux课程学习之我思
陈民禾,原创作品转载请注明出处<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000,我的博客中有一部分是出自M ...