本文参考:
1.https://zhuanlan.zhihu.com/p/180469207;
2.https://javaguide.cn/java/concurrent/java-concurrent-questions-02.html#synchronized-%E5%92%8C-reentrantlock-%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB
作为一道常见面试题,我们首先需要清楚这里的Lock是指哪个Lock?
我们在IDEA中输入Lock会出现三个Lock相关的类,其中一个是接口,其余两个是类。
1.先看sun.misc中的Lock类,源码如下:
package sun.misc;
public class Lock {
private boolean locked = false;
public Lock() {
}
public final synchronized void lock() throws InterruptedException {
while(this.locked) {
this.wait();
}
this.locked = true;
}
public final synchronized void unlock() {
this.locked = false;
this.notifyAll();
}
}
可以看到这个Lock底层还是由synchronized关键字实现的,所以面试问的应该不是这个Lock;
2.再看com.sun下面的这个包,源码如下:
package com.sun.deploy.util;
public class SyncAccess {
public static final int READ_OP = 2;
public static final int WRITE_OP = 4;
public static final int SHARED_READ_MODE = 8;
private int lockedOP = 0;
private Object syncObj = new Object();
private int mode;
public SyncAccess(int var1) {
this.mode = var1;
}
public Lock lock(int var1) {
boolean var2 = false;
byte var3;
if (var1 == 2 && (this.mode & 8) != 0) {
var3 = 4;
} else {
var3 = 6;
}
return new Lock(var3, var1);
}
private void acquireLock(int var1, int var2) {
synchronized(this.syncObj) {
while((this.lockedOP & var1) != 0) {
try {
this.syncObj.wait();
} catch (InterruptedException var6) {
}
}
this.lockedOP |= var2;
}
}
private void releaseLock(int var1) {
synchronized(this.syncObj) {
this.lockedOP &= ~var1;
this.syncObj.notifyAll();
}
}
public class Lock {
private int op;
private Lock(int var2, int var3) {
this.op = var3;
SyncAccess.this.acquireLock(var2, var3);
}
public void release() {
SyncAccess.this.releaseLock(this.op);
}
}
}
同样是由synchronized实现的上锁,因此也不是这个。
3.那么就只剩下concurrent包下的Lock接口了,源码如下:
package java.util.concurrent.locks;
import java.util.concurrent.TimeUnit;
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
而我们常见的Lock实现类ReentrantLock类,可以作为切入点进行对比。
Lock lock = new ReentrantLock();
1.synchronized和Lock的区别
1.底层实现来看:
synchronized: 底层使用指令码方式来控制锁的,映射成字节码指令就是增加来两个指令:monitorenter和monitorexit。当线程执行遇到monitorenter指令时会尝试获取内置锁,如果获取锁则锁计数器+1,如果没有获取锁则阻塞;当遇到monitorexit指令时锁计数器-1,如果计数器为0表示释放了锁;
Lock: 是一个接口,提供了多种实现类,底层是CAS乐观锁;比如ReentrantLock,依赖AbstractQueuedSynchronizer类,把所有的请求线程构成一个CLH队列。而对该队列的操作均通过Lock-Free(CAS)操作。
2.用法:
synchronized: 在需要同步的对象中加入此控制,synchronized可以加在方法上,也可以加在特定代码块中,括号中表示需要锁的对象。
Lock: 一般使用ReentrantLock类做为锁。在加锁和解锁处需要通过lock()和unlock()显示指出。所以一般会在finally块中写unlock()以防死锁。
3.调度:
synchronized: 使用Object对象本身的wait 、notify、notifyAll调度机制;
Lock: 可以使用Condition进行线程之间的调度;
4.锁的类型:
synchronized: 可重入 不可中断 非公平;
Lock: 可重入 可中断 可公平(两者皆可);
5.锁的状态:
synchronized: 无法判断;
Lock: 可以判断;
6.锁的释放(死锁产生)
synchronized: 在发生异常时候会自动释放占有的锁,因此不会出现死锁;
Lock: 发生异常时候,不会主动释放占有的锁,必须手动unlock来释放锁,可能引起死锁的发生;
2.ReentrantLock与synchronized的异同
1.两者都是可重入锁;JDK 提供的所有现成的 Lock 实现类,包括 synchronized 关键字锁都是可重入的;
2.synchronized 依赖于 JVM 而 ReentrantLock 依赖于 API;
3.ReentrantLock 比 synchronized 增加了一些高级功能:
等待可中断 : ReentrantLock提供了一种能够中断等待锁的线程的机制,通过 lock.lockInterruptibly() 来实现这个机制。也就是说正在等待的线程可以选择放弃等待,改为处理其他事情。
可实现公平锁 : ReentrantLock可以指定是公平锁还是非公平锁。而synchronized只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。ReentrantLock默认情况是非公平的,可以通过 ReentrantLock类的ReentrantLock(boolean fair)构造方法来指定是否是公平的。
可实现选择性通知(锁可以绑定多个条件): synchronized关键字与wait()和notify()/notifyAll()方法相结合可以实现等待/通知机制。ReentrantLock类当然也可以实现,但是需要借助于Condition接口与newCondition()方法。