王炸
同一个线程,可以上两次锁就是可重入锁。
只能上一次锁,若二次上锁则造成死锁为不可重入锁。
不可重入锁
package lao.ben.juc.lock;
/**
* 自定义不可重入锁
*/
public class NoAgainLock {
private boolean isLocked = false;
//线程第一次进入后就会将器设置为true,第二次进入是就会由于while true进入死循环
public synchronized void lock() throws InterruptedException {
while (isLocked) {
wait();
}
isLocked = true;
}
//释放锁,接触阻塞
public synchronized void unlock() {
isLocked = false;
notify();
}
}
package lao.ben.juc.lock;
//不可重入锁
public class NoTest {
NoAgainLock lock = new NoAgainLock();
public static void main(String[] args) throws InterruptedException {
new NoTest().doTry();
}
public void doTry() throws InterruptedException {
//第一次上锁,外部锁
lock.lock();
System.out.println("准备执行doReduce()");
//方法内会再次上锁,内部锁
//进入就出不来了
doReduce();
System.out.println("我猜:永远不会走到这一步");
//释放第一次上的锁
lock.unlock();
}
public void doReduce() throws InterruptedException {
//锁上加锁,还都是同一把锁
lock.lock();
System.out.println("执行doJob方法过程中");
lock.unlock();
}
}
可重入锁
package lao.ben.juc.lock;
//可重入锁
//可重入锁增加了计数器以及记录当前拿到锁的线程
public class MyReentrantLock {
//默认没有上锁
boolean isLocked = false;
//记录当前拿到锁的线程
Thread lockedBy = null;
//上锁次数计数
int lockedCount = 0;
/**
* 上锁逻辑
*
* 线程1第一次进入,计数器+1
* 线程2第一次进入,发现没有锁,开始排队。
* 线程1第二次进入,计数器+1
*
*/
public synchronized void lock() throws InterruptedException {
Thread thread = Thread.currentThread();
// 上锁了 并且 如果是同一个线程则放行,否则其它线程需要进入while循环进行等待
while (isLocked && lockedBy != thread) {
wait();
}
//线程1第一次进入就进行上锁
isLocked = true;
//上锁次数计数递增
lockedCount++;
//当前拿到锁的线程为线程1
lockedBy = thread;
}
/**
* 释放锁逻辑
* 当前线程为拿到锁的线程
* 计数器递减
*/
public synchronized void unlock() {
if (Thread.currentThread() == this.lockedBy) {
//将上锁次数减一
lockedCount--;
//当计数为0,说明所有线程都释放了锁
if (lockedCount == 0) {
//真正的释放了所有锁
isLocked = false;
notify();
}
}
}
}
可重入锁的实现原理
与不可重入锁相比,可重入锁本身不那么激进。
为锁关联一个持有者和计数器,以线程为粒度获取锁。
1、张三使用锁A,锁A记录使用者为张三,计数器+1,张三再次使用锁A,计数器再次+1。
2、张三退出同步代码块,计数器逐步递减,直至为0,锁释放。
3、计数器次数代表这个线程现在重入了几次。即只有当计数器为0时,代表所有线程都释放了锁。