Synchronized
作用
以一种简单的策略来防止线程干扰和内存一致性错误,如果一个对象对多个线程可见,该对象变量的所有读取或写入都是通过同步方法完成的。
即:能够保证在同一时刻最多只有一个线程执行该段代码,达到保证并发安全的效果。
Synchronized是Java的关键字,被Java原生支持。
是最基本的互斥同步手段。
用法
- 对象锁
- 方法锁(锁对象为this当前实例对象)
- 同步代码锁(自己指定锁对象)
- 类锁:只有一个Class对象
- synchronized修饰静态方法
- 指定锁为Class对象
多线程访问同步方法的7种情况
1、两个线程同时访问一个对象的同步方法
串行执行
2、两个线程访问的是两个对象的同步方法
并行执行,不是一个对象。
3、两个线程访问的是synchronized的静态方法
串行执行,只有一个Class对象
4、同时访问同步方法和非同步方法
串行执行
5、访问同一个对象的不同的普通同步方法
串行,this是同一个
6、同时访问静态synchronized和非静态的synchronized方法
并行执行,锁对象不是同一个
7、方法抛出异常后,释放锁
synchronized会自动释放,Lock等不会
三个核心思想
- 一把锁只能同时被一个线程获取,没有拿到锁的线程必须等待(对应第1、5种情况)
- 每个实例都对应有自己的一把锁,不同实例之前互不影响;
例外:锁对象是*.class以及synchronized修饰的是static方法的时候,所有对象共用同一把类锁(对应2、3、4、6种情况) - 无论是方法正常执行完毕或者方法抛出异常,都会释放锁(对应第7种情况)
性质
1、可重入:
同一个线程的外层函数获取锁后,内层函数可以直接再次获取该锁。
2、不可中断
原理
1、加锁和释放锁的原理:Monitor
2、可重入原理:加锁次数计数器
3、可见性原理:内存模型
缺点
1、效率低
- 锁的释放情况少
- 视图获取锁时不能设置超时时间
- 不能中断一个正在视图获取锁的线程
2、灵活度较差
- 加锁和释放锁的时机单一
- 每个锁仅有单一的对象
3、无法知道是否成功获取到锁
注意点
1、锁对象不能为空、作用域不宜过大、避免死锁
2、尽量使用JUC包下的类,再考虑Synchronized,再考虑Lock
Monitor是什么
- 是一种用来实现同步的工具
- 与每个Java对象相互关联,:每个java对象都有一个Monitor相对应
- Monitor是实现Sychronized的基础
Monitor与Java对象和线程之间关联
- 如果一个java对象被某个线程锁住,则该java对象的Mark Word字段中LockWord指向monitor的起始地址
- Monitor的Owner字段会存放拥有相关联对象锁的线程id
说明:
java对象在堆中的基本内存结构,分为三个部分:
1、对象头(header):包括Mark Word(标记字段)和Class Pointer(类型指针)
2、实例数据(instance data):对象真正存储的有效信息,即代码中定义的各种类型的字段内容
3、对齐填充(padding):由HotSpot虚拟机定义对象起始地址必须是8字节整数倍,当不是整数倍时,需要填充数据补齐,因为对补齐的数据访问只需要一次内存访问即可
Monitor的结构
- _count:记录owner线程获取锁的次数。这决定了synchronized是可重入的。
- _owner:初始时为NULL表示当前没有任何线程拥有该monitor record,当线程成功拥有该锁后保存线程唯一标识,当锁被释放时又设置为NULL。
- _WaitSet:存放处于wait状态的线程队列
- _EntryList:存放等待锁而被block的线程队列
Monitor的具体实现
- Monitor是在jvm底层实现的,底层代码是c++
- Monitor的enter方法:获取锁
- Monitor的exit方法:释放锁
- Monitor的wait方法:为java的Object的wait方法提供支持
- Monitor的notify方法:为java的Object的notify方法提供支持
- Monitor的notifyAll方法:为java的Object的notifyAll方法提供支持
1 | public class SynchronizedTest { |
javap -verbose SynchronizedTest.class
1 | public void syncTask(); |
存在两个 monitorexit,为了保证在异常时能够释放锁。
另一种形式:
1 | public class SynchronizedTest { |
采用:ACC_SYNCHRONIZED 标识。
1 | public synchronized void syncTask(); |