一、synchronized与Lock的异同
synchronized与Lock都能进行加锁,不过不同的是Synchronized 是Java的一个关键字,而Lock是java.util.concurrent.Locks 包下的一个接口,底下有不同的实现方式。另外,synchronized可以用在代码块上、方法上;而Lock只能写在代码里。
synchronized在代码执行完或出现异常时自动释放锁;Lock不会自动释放锁,需要在finally中显
示释放锁。Lock的实现让加锁行为更加灵活,可响应中断、可判断是否已获取到锁, 而synchronized 关键字没有这些行为。
public class Test01 {
private static final ReentrantLock lock = new ReentrantLock();
public static void main(String[] args){
try {
// 获取锁
lock.lock();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 释放锁
lock.unlock();
}
}
}
二、synchronized的底层实现原理
1、synchronized作用在代码块时,它的底层是通过monitorenter、monitorexit指令来实现的。
- monitorenter:
每个对象都是一个监视器锁(monitor),当monitor被占用时就会处于锁定状态,线程执行
monitorenter指令时尝试获取monitor的所有权,过程如下:
如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor
的所有者。如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1。如果其
他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获
取monitor的所有权。 - monitorexit:
执行monitorexit的线程必须是objectref所对应的monitor持有者。指令执行时,monitor的进入
数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个
monitor阻塞的线程可以尝试去获取这个monitor的所有权。
2、方法的同步并没有通过 monitorenter 和 monitorexit 指令来完成,不过相对于普通方法,其常量
池中多了 ACC_SYNCHRONIZED 标示符。JVM就是根据该标示符来实现方法的同步的:
当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。
三、synchronized可以修饰静态方法和静态代码块吗
synchronized可以修饰静态方法,但不能修饰静态代码块。
当修饰静态方法时,监视器锁(monitor)便是对象的Class实例,因为Class数据存在于永久代,因此
静态方法锁相当于该类的一个全局锁。
四、ReentrantLock的实现原理
ReentrantLock 是基于 AQS 实现的, 即 AbstractQueuedSynchronizer 的缩写,具体的看一看这篇Java并发包的灵魂AbstractQueuedSynchronizer