part8-ReentrantLock学习
目录页:https://blog.****.net/u011294519/article/details/88367808
1. ReentrantLock
1.1.小声哔哔
年轻的时候,说到锁想到的就是synchronized关键字,而且那时候项目要求不高,已经完全能满足需求,但是深入了解synchronized以后发现自己真的是年轻(貌似说synchronized已经被优化的与Lock差不多,但是真的了解过后发现由于synchronized本身的限制导致在读写锁方面是synchronized永远也赶不上的)。
ReentrantLock从命名上来看就是可重入,但是synchronized其实也是可重入的
ReentrantLock与synchronized的区别:
- synchronized是依赖于JVM实现的,而ReenTrantLock是JDK实现的。
- 如果一个方法被Synchronized修饰作为对象锁,甚至是类锁(本质也是对象锁),那么一个线程获取到锁后其他线程就被阻塞,直到该线程释放锁。ReentrantLock支持设置等待时间,并且可阻断。
- 在释放锁的方式上,synchronized在开发过程中完全可以不用去关心,在方法执行完成或执行异常后,JVM会将锁释放,这也是内置关键字的好处。而ReentrantLock则需要我们显示的释放锁,而且必须是要写在finally中防止异常导致死锁。由此可以看出在使用方式上ReenTrantLock比synchronized更加灵活,但是synchronized使用起来更加简单。
1.2. ReentrantLock的一些特性
- ReenTrantLock可以通过构造方法指定使用公平锁还是非公平锁。Synchronized关键字只能是非公平锁。(PS:公平锁就是FIFO这种,但是非公平锁效率会更高,因为公平锁要加判断等进行实现,有这功夫活都干完了)
- ReenTrantLock利用AQS提供了一个Condition(条件)类,可以对线程分组唤醒,而synchronized则是会唤醒全部线程来竞争锁。
- ReenTrantLock可以使用tryLock尝试获取锁,并不是一直阻塞。
1.3.ReentrantLock主要方法
构造方法ReentrantLock():初始化非公平锁
构造方法public ReentrantLock(boolean fair):传入true初始化公平锁,若传入参数false则初始化非公平锁。
lock():获取锁
unlock():释放锁
boolean tryLock():尝试获取锁,若获取成功则返回true
boolean tryLock (long timeout, TimeUnit unit):尝试获取锁,若此时无法获取锁在等待timeout时间后放弃获取锁并返回false。
lockInterruptibly():以响应中断的方式加锁
1.4. 上代码
1.4.1. 实现与synchronized相同效果
先使用ReentrantLock达到和synchronized相同的效果,使用递归体现可重入
package com.concurrent.aqslock.part8.reent;
import java.util.concurrent.locks.ReentrantLock;
/** * 使用显示锁的范式 */ public class LockDemo { private static ReentrantLock lock = new ReentrantLock();
/** * 使用ReentrantLock锁 */ static class ReenTrantThread implements Runnable { private int count = 0;
@Override public void run() { cyclic(); }
private void cyclic() { lock.lock(); try { while (count < 2) { System.out.println(Thread.currentThread().getName() + " count value: " + count); Thread.sleep(1000); ++count; System.out.printf("Lock is get by CurrentThread: %s %s \n", Thread.currentThread().getName(), lock.isHeldByCurrentThread()); cyclic(); Thread.sleep(3000); } } catch (InterruptedException e) { e.printStackTrace(); } finally { if (count >= 2 && lock.isHeldByCurrentThread()) { System.out.printf("%s unlock ,queue length: %s \n",Thread.currentThread().getName(),lock.getQueueLength()); lock.unlock(); } } } }
/** * 使用synchronized锁 */ static class SysThread implements Runnable { private int count = 0;
@Override public void run() { cyclic(); }
private synchronized void cyclic() { while (count < 2) { System.out.println(Thread.currentThread().getName() + " count value: " + count); ++count; cyclic(); } } }
public static void main(String[] arg0) throws InterruptedException { for (int i = 0; i < 2; ++i) { new Thread(new ReenTrantThread(), "ReenTrantThread " + i).start(); }
for (int i = 0; i < 2; ++i) { new Thread(new SysThread(), "SysThread " + i).start(); }
Thread.sleep(3000); }
} |
运行结果:
代码位置:aqs-lock的part8
1.4.2. 使用可中断式获取锁
package com.concurrent.aqslock.part8.reent;
import java.util.concurrent.locks.ReentrantLock;
/** * 演示可中断式获取锁 */ public class InterruptReenTrant { private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] arg0) {
Thread t1 = new Thread(() -> { try { System.out.println(Thread.currentThread().getName() + " before get lock"); lock.lockInterruptibly(); Thread.sleep(3000); System.out.println(Thread.currentThread().getName() + " after get lock");
} catch (InterruptedException e) { e.printStackTrace(); } finally { if(lock.isHeldByCurrentThread()){ System.out.println(Thread.currentThread().getName()+" unlock"); lock.unlock(); } } }, "first thread");
Thread t2 = new Thread(() -> { try { System.out.println(Thread.currentThread().getName() + " before get lock"); lock.lockInterruptibly(); Thread.sleep(2000); System.out.println(Thread.currentThread().getName() + " after get lock");
} catch (InterruptedException e) { e.printStackTrace(); } finally { if(lock.isHeldByCurrentThread()){ System.out.println(Thread.currentThread().getName()+" unlock"); lock.unlock(); } } }, "second thread");
t1.start(); t2.start(); // 尝试注释和不注释下面代码 t1.interrupt(); }
} |
运行结果:
代码位置:aqs-lock的part8
1.4.3. tryLock
package com.concurrent.aqslock.part8.reent;
import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock;
/** * 演示tryLock */ public class TryLockReenTrant { private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] arg0) { Thread t1 = new Thread(() -> { try { System.out.println(Thread.currentThread().getName() + " before try lock"); if(lock.tryLock()){ Thread.sleep(3000); System.out.println(Thread.currentThread().getName() + " after try lock"); }
} catch (InterruptedException e) { e.printStackTrace(); } finally { if(lock.isHeldByCurrentThread()){ System.out.println(Thread.currentThread().getName()+" unlock"); lock.unlock(); } } }, "first thread");
Thread t2 = new Thread(() -> { try { System.out.println(Thread.currentThread().getName() + " before try lock"); if(lock.tryLock()){ Thread.sleep(2000); System.out.println(Thread.currentThread().getName() + " after try lock"); } else { System.out.println(Thread.currentThread().getName() + " try lock fail"); } } catch (InterruptedException e) { e.printStackTrace(); } finally { if(lock.isHeldByCurrentThread()){ System.out.println(Thread.currentThread().getName()+" unlock"); lock.unlock(); } } }, "second thread");
Thread t3 = new Thread(() -> { try { System.out.println(Thread.currentThread().getName() + " before try lock"); if(lock.tryLock(6L, TimeUnit.SECONDS)){ Thread.sleep(2000); System.out.println(Thread.currentThread().getName() + " after try lock"); } else { System.out.println(Thread.currentThread().getName() + " try lock fail"); } } catch (InterruptedException e) { e.printStackTrace(); } finally { if(lock.isHeldByCurrentThread()){ System.out.println(Thread.currentThread().getName()+" unlock"); lock.unlock(); } } }, "third thread");
t1.start(); t2.start(); t3.start(); } } |
运行结果:
代码位置:aqs-lock的part8
1.5. 看代码
非公平队列的Lock():
可以看到,使用了我们的老朋友AQS的compareAndSetState方法,若state为0时则置为1,设置锁的拥有者为当前线程。
若更新state失败,则进入acquire方法
若获取到锁的为当前现在,则给state加1,并直接返回
若获取到锁的不是当前线程,则进入acquireQueued方法
首先将当前线程加入等待队列,并挂起当前线程。