synchronized 和 ReentrantLock 区别是什么?
答:
主要区别如下:
-
ReentrantLock 只适用于代码块锁,而 synchronized 可用于修饰方法、代码块等。
-
synchronized 竞争锁时会一直等待;ReentrantLock 可以尝试获取锁,并得到获取结果
-
超时获取锁的特性:synchronized 获取锁无法设置超时;ReentrantLock 可以设置获取锁的超时时间
-
synchronized 无法实现公平锁;ReentrantLock 可以满足公平锁,即先等待先获取到锁
-
synchronized 控制等待和唤醒需要结合加锁对象的 wait() 和 notify()、notifyAll();ReentrantLock 控制等待和唤醒需要结合 Condition 的 await() 和 signal()、signalAll() 方法
-
synchronized 是 JVM 层面实现的;ReentrantLock 是 JDK 代码层面实现
-
便利性:synchronized 在加锁代码块执行完或者出现异常,自动释放锁;ReentrantLock 不会自动释放锁,需要手动在 finally{} 代码块显示释放
相同点:都可以做到同一线程,同一把锁,可重入代码块。
原文链接: https://www.cnblogs.com/ConstXiong/p/12014904.html (概念)
原文链接: https://www.cnblogs.com/jlutiger/p/10548291.html (底层原理)
原文链接: https://blog.csdn.net/zmx729618/article/details/51594166 (代码示例演示)
拓展:
ReentrantLock称为重入锁,位于JUC包的locks,和CountDownLatch、FutureTask一样基于AQS实现。能够实现比synchronized更细粒度的控制,比如控制公平性。此外需要注意,调用lock()之后,必须调用unlock()释放锁。它的性能未必比synchronized高,并且是可重入的。
在Java 6之后,synchronized性能得到很大提升。主要是因为引入了:
1:Adaptive spinning(自适应自旋)
2:Lock Eliminate(锁消除)
3:Lock Coarsening(锁粗化)
4:Lightweight Locking(轻量级锁)
5:Biased Locking(偏向锁)
6:......
公平锁与非公平锁
按照申请锁的顺序来一次获得锁称为公平锁.synchronized的是非公平锁,ReentrantLock可以通过构造函数实现公平锁. new RenentrantLock(boolean fair)
实例演示
ReentrantLock 的lock机制有2种,忽略中断锁和响应中断锁,这给我们带来了很大的灵活性。比如:如果A、B两个线程去竞争锁,A线程得到了锁,B线程等待,但是A线程这个时候实在有太多事情要处理,就是一直不返回,B线程可能就会等不及了,想中断自己,不再等待这个锁了,转而处理其他事情。这个时候ReentrantLock就提供了两种机制:
一、B线程中断自己(或者别的线程中断它),但ReentrantLock 不去响应,让B线程继续等待,你再怎么中断,我全当耳边风(synchronized原语就是如此);
二、B线程中断自己(或者别的线程中断它),ReentrantLock 处理了这个中断,并且不再等待这个锁的到来,完全放弃。
请看例子:
package zmx.multithread.test.reentrantlock;
import java.util.concurrent.locks.ReentrantLock;
/**
*
* @author zhangwenchao
*
*/
public class ReentrantLockTest {
//是用ReentrantLock,还是用synchronized
public static boolean useSynchronized = false;
public static void main(String[] args) {
IBuffer buff = null;
if(useSynchronized){
buff = new Buffer();
}else{
buff = new BufferInterruptibly();
}
final Writer writer = new Writer(buff);
final Reader reader = new Reader(buff);
writer.start();
reader.start();
new Thread(new Runnable() {
public void run() {
long start = System.currentTimeMillis();
for (;;) {
// 等5秒钟去中断读
if (System.currentTimeMillis() - start > 5000) {
System.out.println("不等了,尝试中断");
reader.interrupt();
break;
}
}
}
}).start();
}
}
interface IBuffer{
public void write();
public void read() throws InterruptedException;
}
class Buffer implements IBuffer{
private Object lock;
public Buffer() {
lock = this;
}
public void write() {
synchronized (lock) {
long startTime = System.currentTimeMillis();
System.out.println("开始往这个buff写入数据…");
for (;;)// 模拟要处理很长时间
{
if (System.currentTimeMillis() - startTime > Integer.MAX_VALUE)
break;
}
System.out.println("终于写完了");
}
}
public void read() {
synchronized (lock) {
System.out.println("从这个buff读数据");
}
}
}
class BufferInterruptibly implements IBuffer{
private ReentrantLock lock = new ReentrantLock();
public void write() {
lock.lock();
try {
long startTime = System.currentTimeMillis();
System.out.println("开始往这个buff写入数据…");
for (;;)// 模拟要处理很长时间
{
if (System.currentTimeMillis() - startTime > Integer.MAX_VALUE)
break;
}
System.out.println("终于写完了");
} finally {
lock.unlock();
}
}
public void read() throws InterruptedException{
lock.lockInterruptibly();// 注意这里,可以响应中断
// lock.lock();// 注意这里,不可以响应中断
try {
System.out.println("从这个buff读数据");
} finally {
lock.unlock();
}
}
}
class Writer extends Thread {
private IBuffer buff;
public Writer(IBuffer buff) {
this.buff = buff;
}
@Override
public void run() {
buff.write();
}
}
class Reader extends Thread {
private IBuffer buff;
public Reader(IBuffer buff) {
this.buff = buff;
}
@Override
public void run() {
try {
buff.read();
} catch (InterruptedException e) {
System.out.println("我不读了");
}
System.out.println("读结束");
}
}
1) 如果使用lock.lockInterruptibly();指定可以响应中断,则输出如下:
开始往这个buff写入数据…
不等了,尝试中断
我不读了
读结束`
则:获取到中断异常,执行中断异常处理程序。
- 如果使用lock.lock();指定不可以响应中断,则输出如下:
开始往这个buff写入数据…
不等了,尝试中断
则:不能获取到中断异常,线程等待。
示例二:
package zmx.multithread.test.reentrantlock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class T2{
public static void main(String[] args){
Thread i1 = new Thread(new RunIt3());
Thread i2 = new Thread(new RunIt3());
i1.start();
i2.start();
i2.interrupt(); //中断
}
}
class RunIt3 implements Runnable{
private static Lock lock = new ReentrantLock();
public void run(){
try{
//---------a--------------------------
//lock.lock();
lock.lockInterruptibly();
//lock.tryLock();
//lock.tryLock(5,TimeUnit.SECONDS);
System.out.println(Thread.currentThread().getName() + " running");
TimeUnit.SECONDS.sleep(10);
System.out.println(Thread.currentThread().getName() + " finished");
lock.unlock();
}catch (InterruptedException e){
System.out.println(Thread.currentThread().getName() + " interrupted");
}
}
}
如果a处是lock.lock(); 输出:
Thread-0 running
(这里休眠了10s)
Thread-0 finished
Thread-1 running
Thread-1 interrupted
============================
如果a处是lock.lockInterruptibly();输出:
Thread-0 running
Thread-1 interrupted
(这里休眠了10s)
Thread-0 finished