Synchronized 关键字
synchronized
同步方法支持一种简单的策略来防止线程干扰和内存一致性错误:即如果一个对象对多个线程可见,则该对象变量的所有读写操作都是通过同步方法完成。
被 synchronized
修饰的代码段,能够保证在同一时刻最多只有一个线程执行,以达到保证并发安全的效果。
用法
1 对象锁
1.1 代码块形式
手动指定锁对象
static final Object LOCK_1 = new Object();
synchronized (LOCK_1) {
// do method
}
1.2 方法锁形式
synchronized
修饰方法,默认锁对象为this
private synchronized void doMethod() {
// do method
}
2 类锁
Java 类可能会有很多个对象,但只有1个 Class 对象。类锁,相当于 Class 对象的锁。即类锁只能在同一时刻被一个对象拥有。
2.1 静态方法锁
private static synchronized void doMethod() {
// do method
}
2.1 synchronized(*.class) 代码块
synchronized
修饰静态的方法或指定锁为 Class 对象
public class SyncClassClass {
private void doMethod() {
synchronized (SyncClassClass.class) {
// do method
}
}
}
多线程访问同步方法的面试点
1 多线程同时访问一个对象的synchronized方法
多线程会发生对同步方法锁的竞争,当一个线程竞争到锁时,其余线程会阻塞等待。
2 多线程访问的是两个对象的synchronized方法
因为对象锁的方法锁,默认为this当前实例对象,因此每一个线程的锁相互独立,并不互斥,会发生并发问题。
3 多线程同时访问一个对象的静态synchronized方法
多线程会发生对同步方法锁的竞争,当一个线程竞争到锁时,其余线程会阻塞等待。
4 多线程同时访问一个对象的synchronized方法与非同步方法
synchronized
只作用于同步方法,非同步方法不受影响,因此同步方法与非同步方法可以并行。
5 多线程同时访问一个对象的不同的synchronized方法
因为对象锁的方法锁,默认为this当前实例对象,因此对于同一个实例对象,同步锁会发生互斥,不同的同步方法将会串行执行。
6 多线程同时访问一个对象的静态synchronized与非静态synchronized方法
静态synchronized
锁为类锁,非静态synchronized
锁为对象锁,类锁与对象锁之间不会发生互斥,因此静态synchronized
方法和非静态synchronized
方法将会同时执行。
7 synchronized方法抛异常后,会释放锁
synchronized
:抛异常后会自动释放锁。Lock
:抛异常不会主动释放锁。
性质
1 可重入
指同一线程的外层函数获得锁之后,内层函数可以直接再次获取该锁。
好处:避免死锁,提升封装性。
粒度:线程非调用。
原理:利用加锁次数计数器。即线程第一次给对象加锁的时候,计数变为1,每当这个相同的线程在此对象上再次获得锁时,计数会递增;每当任务离开时,计数会递减,当计数为0时,锁被完全释放。
2 不可中断
当一个锁被某一线程持有时,其余线程只能阻塞等待该线程释放锁。如果该线程不释放锁,则其余线程只能永远等待。
内存可见性
缺点
- 效率低:锁的释放情况少、试图获得锁时不能设定超时、不能中断一个正在试图获得锁的线程。
- 不够灵活:加锁和释放的时机单一,每个锁仅有单一的条件。
- 无法知道是否成功获取到锁