synchronized
synchronized
是一个 JVM 提供实现的内部锁机制。一个 synchronized
块包括一个锁对象引用和代码块。
当 synchronized
作用在方法上时,其锁对象是方法所在对象本身。
使用 synchronized
时至多只有一个线程可以获得锁。当线程进入 synchronized
代码块时自动获取锁,离开代码块时自动释放锁。
synchronized
同时也是一个支持可重入的互斥锁,同一个线程可以多次进入同步块。
public class WaitNotify {
static boolean flag = true;
static Object lock = new Object();
public static void main(String args[]) throws InterruptedException {
Thread waitThread = new Thread(new Wait(), "WaitThread");
waitThread.start();
TimeUnit.SECONDS.sleep(1);
Thread notifyThread = new Thread(new Notify(), "NotifyThread");
notifyThread.start();
}
static class Wait implements Runnable {
@Override
public void run() {
synchronized (lock) { //获取锁
while (flag) {
try {
System.out.println(Thread.currentThread() + " flag is true. wait @"
+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
//等待, 并释放锁
lock.wait();
//重新获得锁之后从 wait() 返回
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread() + " flag is false. running @ " +
new SimpleDateFormat("HH:mm:ss").format(new Date()));
}
}
}
static class Notify implements Runnable {
@Override
public void run() {
synchronized (lock) {
System.out.println(Thread.currentThread() + " hold lock. notify @ "
+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
//通知等待的线程
lock.notifyAll();
flag= false;
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
//直到同步块结束才释放锁给 wait() 线程
}
synchronized (lock) {
System.out.println(Thread.currentThread() + " hold lock again. notify @ "
+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
}
}
}
}
ReentrantLock
ReentrantLock
也是一个可重入的互斥锁。 它是一个J.U.C包中实现了 Lock
接口的类。Lock
接口定义了下面一些方法:
void lock(); //获取锁
void lockInterruptibly(); //可被中断获取锁
Condition newCondition(); //返回一个绑定在此锁上的 condition 实例
boolean tryLock(); //尝试获取锁,立即返回结果
boolean tryLock(long time, TimeUnit unit); // 定时获取锁,不可被中断
void unlock(); //释放锁
ReentrantLock
对比内部锁首先最明显的一个区别就是 ReentrantLock
锁的获取和释放都需要显示地手动调用方法。
获取锁时调用 lock()
或 tryLock()
,释放锁时调用 unlock()
.
ReentrantLock
锁的实现是通过和继承自 AQS 的同步器组合使用实现的,而非JVM实现。
ReentrantLock
对锁的控制程度比 synchronized
要高,更灵活,在获取锁时可以中断线程,或者设置最大等待时间等等。
public class LockTest {
private Lock lock = new ReentrantLock();
public void do1() throws InterruptedException {
try {
lock.lock();
//do something...
} finally {
lock.unlock();
}
}
public void do2() {
try{
lock.lock();
//do something...
} finally {
lock.unlock();
}
}
}
小结
通过一个表格来对比一下两者区别
项目 | synchronized | ReentrantLock |
---|---|---|
锁实现层次 | java关键字,JVM实现 | J.U.C下面的一个类,结合 AQS 实现的锁机制 |
锁获取 | 进入代码块自动获取 | 手动调用方法lock() 或 tryLock() 获取 |
锁释放 | 离开代码块自动释放 | 手动调用方法 unlock() 释放 |
锁类型 | 可重入,不可中断、非公平 | 可重入、可中断、公平或非公平可控 |
等待队列 | 只等有一个等待队列 | 通过组合 Condition 实现多个等待队列 |
参考:
https://blog.csdn.net/u012403290/article/details/64910926?locationNum=11&fps=1