>最近面试经常遇到这个问题,因为我对这方面没有太过深入去了解,回答的不是很好,所以记录一下吧。
>
>多看书,之前看的《Java并发编程的艺术》再复习一遍~
### 1. 底层实现
从底层实现上来说,synchronized 是JVM层面的锁,是Java关键字,通过monitor对象来完成(monitorenter与monitorexit),对象只有在同步块或同步方法中才能调用wait/notify方法。
ReentrantLock 是从jdk1.5以来(java.util.concurrent.locks.Lock)提供的API层面的锁。
synchronized 的实现涉及到锁的升级,具体为无锁、偏向锁、轻量级锁、重量级锁这四种状态。
>锁升级过程看这个链接:[关于 锁的四种状态与锁升级过程 图文详解](https://www.cnblogs.com/mingyueyy/p/13054296.html)
ReentrantLock 实现则是通过利用CAS(CompareAndSwap)自旋机制保证线程操作的原子性和 volatile 保证数据可见性以实现锁的功能。
```Java
synchronized (new Object()){
}
new ReentrantLock();
```
使用javap -c对如上代码进行反编译得到如下代码:

### 2. 是否可手动释放
synchronized 不需要用户去手动释放锁,synchronized 代码执行完后系统会自动让线程释放对锁的占用;
ReentrantLock 则需要用户去手动释放锁,如果没有手动释放锁,就可能导致死锁现象。一般通过 lock() 和 unlock() 方法配合 try/finally 语句块来完成,使用释放更加灵活。
```Java
private int number = 0;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private AtomicInteger atomicInteger;
public void increment() throws Exception {
lock.lock();
try {
while (number != 0) {
condition.await();
}
//do something
number++;
System.out.println(Thread.currentThread().getName() + "\t" + number);
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
```
### 3. 是否可中断
synchronized 是不可中断类型的锁,除非加锁的代码中出现异常或正常执行完成; ReentrantLock 则可以中断,可通过 trylock(long timeout,TimeUnit unit) 设置超时方法或者将 lockInterruptibly() 放到代码块中,调用 interrupt 方法进行中断。
```Java
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
```
### 4. 是否公平锁
>什么是公平锁、非公平锁?[说一下公平锁和非公平锁的区别?](https://blog.csdn.net/qq_35190492/article/details/104943579)
synchronized 为非公平锁;
ReentrantLock 则即可以选公平锁也可以选非公平锁,通过构造方法 new ReentrantLock 时传入 boolean 值进行选择,为空默认 false 非公平锁,true 为公平锁。
```Java
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
```
### 5. 锁是否可绑定条件Condition
synchronized 不能绑定;
ReentrantLock 通过绑定 Condition 结合 await()/singal() 方法实现线程的精确唤醒,而不是像 synchronized 通过Object类的 wait()/notify()/notifyAll() 方法要么随机唤醒一个线程要么唤醒全部线程。
示例:用ReentrantLock绑定三个条件实现线程A打印一次1,线程B打印两次2,线程C打印三次3
```
public class TestLock {
private int number = 1;//A:1 B:2 C:3
private Lock lock = new ReentrantLock();
private Condition c1 = lock.newCondition();
private Condition c2 = lock.newCondition();
private Condition c3 = lock.newCondition();
//1 判断
public void print1() {
lock.lock();
try {
//判断
while (number != 1) {
c1.await();
}
//2 do sth
for (int i = 1; i < 2; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + number);
}
//3 通知
number = 2;
c2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
//1 判断
public void print2() {
lock.lock();
try {
//判断
while (number != 2) {
c2.await();
}
//2 do sth
for (int i = 1; i < 3; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + number);
}
//3 通知
number = 3;
c3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
//1 判断
public void print3() {
lock.lock();
try {
//判断
while (number != 3) {
c3.await();
}
//2 do sth
for (int i = 1; i < 4; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + number);
}
//3 通知
number = 1;
c1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
TestLock resource = new TestLock();
new Thread(()->{
for (int i = 1; i <= 2; i++) {
resource.print1();
}
},"A").start();
new Thread(()->{
for (int i = 1; i <= 2; i++) {
resource.print2();
}
},"B").start();
new Thread(()->{
for (int i = 1; i <= 2; i++) {
resource.print3();
}
},"C").start();
}
}
```
输出结果为:
A 1 B 2 B 2 C 3 C 3 C 3 A 1 B 2 B 2 C 3 C 3 C 3
### 6. 锁的对象
synchronzied 锁的是对象,锁是保存在对象头里面的,根据对象头数据来标识是否有线程获得锁/争抢锁;
ReentrantLock 锁的是线程,根据进入的线程和 int 类型的 state 标识锁的获得/争抢。
>扩展知识:[Java AQS底层原理解析](https://segmentfault.com/a/1190000020521611#:~:text=AQS%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86.%20AQS%20%28AbstractQueuedSynchronizer%29%20%E6%98%AF%E4%B8%80%E4%B8%AA%E6%8A%BD%E8%B1%A1%E5%90%8C%E6%AD%A5%E9%98%9F%E5%88%97%EF%BC%8C%20JUC,%28java.util.concurrent%29%20%E4%B8%AD%E5%BE%88%E5%A4%9A%E5%90%8C%E6%AD%A5%E9%94%81%E9%83%BD%E6%98%AF%E5%9F%BA%E4%BA%8E%20AQS%20%E5%AE%9E%E7%8E%B0%E7%9A%84%E3%80%82.%20AQS%20%E7%9A%84%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86%E5%B0%B1%E6%98%AF%E5%BD%93%E4%B8%80%E4%B8%AA%E7%BA%BF%E7%A8%8B%E8%AF%B7%E6%B1%82%E5%85%B1%E4%BA%AB%E8%B5%84%E6%BA%90%E7%9A%84%E6%97%B6%E5%80%99%E4%BC%9A%E5%88%A4%E6%96%AD%E6%98%AF%E5%90%A6%E8%83%BD%E5%A4%9F%E6%88%90%E5%8A%9F%E6%93%8D%E4%BD%9C%E8%BF%99%E4%B8%AA%E5%85%B1%E4%BA%AB%E8%B5%84%E6%BA%90%EF%BC%8C%E5%A6%82%E6%9E%9C%E5%8F%AF%E4%BB%A5%E5%B0%B1%E4%BC%9A%E6%8A%8A%E8%BF%99%E4%B8%AA%E5%85%B1%E4%BA%AB%E8%B5%84%E6%BA%90%E8%AE%BE%E7%BD%AE%E4%B8%BA%E9%94%81%E5%AE%9A%E7%8A%B6%E6%80%81%EF%BC%8C%E5%A6%82%E6%9E%9C%E5%BD%93%E5%89%8D%E5%85%B1%E4%BA%AB%E8%B5%84%E6%BA%90%E5%B7%B2%E7%BB%8F%E8%A2%AB%E9%94%81%E5%AE%9A%E4%BA%86%EF%BC%8C%E9%82%A3%E5%B0%B1%E6%8A%8A%E8%BF%99%E4%B8%AA%E8%AF%B7%E6%B1%82%E7%9A%84%E7%BA%BF%E7%A8%8B%E9%98%BB%E5%A1%9E%E4%BD%8F%EF%BC%8C%E4%B9%9F%E5%B0%B1%E6%98%AF%E6%94%BE%E5%88%B0%E9%98%9F%E5%88%97%E4%B8%AD%E7%AD%89%E5%BE%85%E3%80%82.)
>原文链接:https://zhuanlan.zhihu.com/p/126085068

谈谈synchronized与ReentrantLock的区别?