ReentrantLock
ReentrantLock
相对于 synchronized
- 可中断
- 可以设置超时时间
- 可以设置公平锁 (防止饥饿)
- 支持多个条件变量 (类似于支持多个 waitSet)
与synchronized一样 ,都支持可重入
基本语法
ReentrantLock reentrantLock = new ReentrantLock();
reentrantLock.lock();
try
{
//临界区
}finally {
reentrantLock.unlock();
}
可重入锁
可重入是指同一个线程如果首个获取了这把锁,那么因为他是这把锁的拥有者,因此有权利再次获取这把锁
如果不是可重入锁,那么第二次获取锁,自己也会被挡住
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
lock.lock(); //lock加锁是不可打断的
try {
log.info("main ");
m1();
}finally {
lock.unlock();
}
}
static void m1(){
lock.lock();
try {
log.info("m1 ");
m2();
}finally {
lock.unlock();
}
}
static void m2(){
lock.lock();
try {
log.info("m2");
}finally {
lock.unlock();
}
}
可打断
被动的被打断 避免死等
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
// 没有竞争获取锁
//有竞争进入阻塞队列,可以被其他线程用 interruput 打断
try {
log.info("尝试获得锁");
lock.lockInterruptibly(); //可打断锁 防止无限制等待 ,可以防止死锁
} catch (InterruptedException e) {
e.printStackTrace();
log.info("没有获得锁,返回");
return;
}
try {
log.info("获取到锁");
} finally {
lock.unlock();
}
}, "t1");
lock.lock();
t1.start();
Thread.sleep(1000);
log.info("打断t1");
t1.interrupt();
}
锁超时
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
//if(!lock.tryLock()) //尝试获得锁 成功获得锁 失败不去阻塞队列等待 防止无限制等待
try {
if(!lock.tryLock(2,TimeUnit.SECONDS)) //设置等待时间 也支持可打断的特性
{
log.info("获取不到锁");
return;
}
} catch (InterruptedException e) {
e.printStackTrace();
log.info("没有获取锁,返回");
return;
}
try {
log.info("获取到锁");
}finally {
lock.unlock();
}
}, "t1");
lock.lock();
t1.start();
}
公平锁
RenntranLock 默认是不公平锁
公平锁 可以解决饥饿问题 , 设置公平锁会降低并发度
public ReentrantLock(boolean fair) 构造函数传入是否是公平锁
条件变量
synchronized 中也有条件变量,就是waitSet休息室,当条件不满足时进入waitSet等待
ReentranLock的条件变量比synchronized强大之处在于,支持多个条件变量
- synchronized 时那些不满足条件的线程都在一间休息室等消息
- 而ReentranLock支持多间休息室,可以分类
使用流程
- await 前需要获取锁
- await 执行后,会释放锁,静茹conditionObject等待
- await 的线程被唤醒(或打断,超时)重新竞争lock锁
- 竞争lock锁成功后,从await后继续执行
static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
//创建一个姓的条件变量 (休息室)
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
lock.lock();
//进入休息室等待
condition1.await();
condition1.signal(); //唤醒等待的线程
condition2.signalAll(); // 唤醒所有等待的线程
}
同步线程之顺序控制
- 使用synchrinized wait() notifyAll()
static final Object lock = new Object();
static boolean canRun = false;
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (lock)
{
while (!canRun) //防止虚假唤醒
{
try {
lock.wait(); // 不允许打印时 等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.info("1");
}
}, "t1");
Thread thread2 = new Thread(() -> {
synchronized (lock)
{
log.info("2");
canRun = true; //设置允许打印
lock.notifyAll(); //唤醒所有等待线程
}
}, "t2");
thread1.start();
thread2.start();
}
- 使用RenntrinLock
static final ReentrantLock lock = new ReentrantLock();
static Condition condition = lock.newCondition();
static boolean canRun = false;
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
try {
while (!canRun && lock.tryLock()) //尝试获得锁
{
condition.await();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
log.info("1");
}, "t1");
Thread thread2 = new Thread(() -> {
lock.lock();
try {
log.info("2");
canRun = true;
condition.signal();
}finally {
lock.unlock();
}
}, "t2");
thread1.start();
thread2.start();
}
- 使用park 和 unpark
static boolean canRun = false;
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
while (!canRun)
{
LockSupport.park();
}
log.info("1");
}, "t1");
Thread thread2 = new Thread(() -> {
log.info("2");
canRun = true;
LockSupport.unpark(thread1);
}, "t2");
thread1.start();
thread2.start();
}
同步线程之交替输出
三个线程 t1输出 a t2输出 b t3输出c 让他们交替输出 abcabcabcabcabc 每个线程输出五次
- 使用synchrinized wait() notifyAll()
public static void main(String[] args) {
WaitNatify waitNatify = new WaitNatify(1,5);
Thread thread1 = new Thread(() -> {
waitNatify.print("a",1,2);
}, "t1");
Thread thread2 = new Thread(() -> {
waitNatify.print("b",2,3);
}, "t2");
Thread thread3 = new Thread(() -> {
waitNatify.print("c",3,1);
}, "t3");
thread1.start();
thread2.start();
thread3.start();
}
}
class WaitNatify{
private int flag;
private int loopNum;
public WaitNatify(int flag, int loopNum) {
this.flag = flag;
this.loopNum = loopNum;
}
public void print(String str,int waitFlag,int nextFlag) {
for (int i = 0; i < loopNum; i++) {
synchronized (this)
{
while (flag != waitFlag)
{
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.printf(str);
flag = nextFlag;
this.notifyAll();
}
}
}
}
- 使用ReentrinLock
public static void main(String[] args) throws InterruptedException {
AwaitSignalAll awaitSignalAll = new AwaitSignalAll(5);
Condition a = awaitSignalAll.newCondition();
Condition b = awaitSignalAll.newCondition();
Condition c = awaitSignalAll.newCondition();
Thread thread1 = new Thread(() -> {
awaitSignalAll.print("a", a,b);
}, "t1");
Thread thread2 = new Thread(() -> {
awaitSignalAll.print("b", b,c);
}, "t2");
Thread thread3 = new Thread(() -> {
awaitSignalAll.print("c", c,a);
}, "t3");
thread1.start();
thread2.start();
thread3.start();
Thread.sleep(1000);
awaitSignalAll.lock(); // 这里不获得锁 引起java.lang.IllegalMonitorStateException异常 (非法监视器状态异常)
try{
a.signalAll(); //目前线程全部在休息室等待 , 这里设置 第一次被唤醒的休息室的线程
}finally {
awaitSignalAll.unlock();
}
}
}
class AwaitSignalAll extends ReentrantLock {
private int loopNum;
public AwaitSignalAll(int loopNum) {
this.loopNum = loopNum;
}
public void print(String str, Condition current,Condition next) {
for (int i = 0; i < loopNum; i++) {
lock();
try {
current.await(); //三个线程调用 全部进入自己的休息室等待
System.out.printf(str); //等待结束 打印str
next.signalAll(); // 唤醒下一个应该执行的休息室内的线程
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
unlock();
}
}
}
}
- 使用park 和 unpark
static Thread thread1;
static Thread thread2;
static Thread thread3;
public static void main(String[] args) throws InterruptedException {
ParkUnpark parkUnpark = new ParkUnpark(5);
thread1 = new Thread(() -> {
parkUnpark.print("a", thread2);
}, "t1");
thread2 = new Thread(() -> {
parkUnpark.print("b", thread3);
}, "t2");
thread3 = new Thread(() -> {
parkUnpark.print("c", thread1);
}, "t3");
thread1.start();
thread2.start();
thread3.start();
LockSupport.unpark(thread1);
}
}
class ParkUnpark {
private int loopNum;
public ParkUnpark(int loopNum) {
this.loopNum = loopNum;
}
public void print(String str, Thread next) {
for (int i = 0; i < loopNum; i++) {
LockSupport.park();
System.out.printf(str);
LockSupport.unpark(next);
}
}
}
AQS AbstractQueuedSynchronizer
全称是 AbstractQueuedSynchronizer,是阻塞式锁和相关的同步器工具的框架
- 用 state 属性来表示资源的状态(分独占模式和共享模式),子类需要定义如何维护这个状态,控制如何获取锁和释放锁
- getState - 获取 state 状态
- setState - 设置 state 状态
- compareAndSetState - cas 机制设置 state 状态
- 独占模式是只有一个线程能够访问资源,而共享模式可以允许多个线程访问资源
- 提供了基于 FIFO 的等待队列,类似于 Monitor 的 EntryList
- 条件变量来实现等待、唤醒机制,支持多个条件变量,类似于 Monitor 的 WaitSet
子类主要实现这样一些方法(默认抛出 UnsupportedOperationException)
- tryAcquire //获取锁
- tryRelease // 释放锁
- tryAcquireShared
- tryReleaseShared
- isHeldExclusively
// 如果获取锁失败
if (!tryAcquire(arg)) {
// 入队, 可以选择阻塞当前线程 实际用了 park unpark 方法
}
// 如果释放锁成功
if (tryRelease(arg)) {
// 让阻塞线程恢复运行
}
不可重入锁
public static void main(String[] args) {
Mylock mylock = new Mylock();
new Thread(() -> {
mylock.lock();
try {
System.out.println("枷锁成功");
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
mylock.unlock();
System.out.println("解锁成功");
}
}).start();
new Thread(() -> {
mylock.lock();
try {
System.out.println("枷锁成功");
}finally {
mylock.unlock();
System.out.println("解锁成功");
}
}).start();
}
}
// 自定义不可重入锁
class Mylock implements Lock {
//同步器类 独占锁
class MySync extends AbstractQueuedLongSynchronizer{
@Override // 获取锁
protected boolean tryAcquire(long arg) {
if(compareAndSetState(0,1))
{
// 加锁成功 设置线程位当前线程
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
@Override //释放锁
protected boolean tryRelease(long arg) {
setExclusiveOwnerThread(null);
setState(0); // state 是volatile 修饰,具有写屏障 保证之前的变量可见性
return true;
}
@Override //是否持有独占锁
protected boolean isHeldExclusively() {
return getState() == 1;
}
// 创建条件变量
public Condition newCondition(){
return new ConditionObject();
}
}
private MySync sync = new MySync();
@Override // 加锁 (不成功进入等待队列 )
public void lock() {
sync.acquire(1);
}
@Override // 可打断锁
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
@Override // 可打断锁 尝试一次
public boolean tryLock() {
return sync.tryAcquire(1);
}
@Override // 尝试加锁 (带超时时间)
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1,unit.toNanos(time));
}
@Override // 解锁
public void unlock() {
sync.release(1);
}
@Override // 创建条件变量
public Condition newCondition() {
return sync.newCondition();
}
}
ReentrinLock原理
构造器
public ReentrantLock() {
sync = new NonfairSync(); // 非公平锁
}
// 加锁方法
final void lock() {
if (compareAndSetState(0, 1)) // 改变 锁状态为加锁
setExclusiveOwnerThread(Thread.currentThread()); //更改锁线程为当前线程
else
acquire(1); // 加锁失败
}
public final void acquire(int arg) {
// 尝试加锁 放入阻塞队列 addWaiter ()
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
尝试加锁
acquireQueued
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) { // 死循环 尝试获得锁
final Node p = node.predecessor(); //前驱节点
if (p == head && tryAcquire(arg)) { // 如果前驱节点是头节点 说明头节点是第二的 才有资格尝试获得锁
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) && // 尝试获取锁失败
parkAndCheckInterrupt()) //阻塞
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
释放锁