偶然看到公众号推荐了一篇重入锁和非重入锁,结合它写的可重入锁 案例一起探讨一下?
1.重入锁和不可重入锁?
可重入锁:当一个线程获取对象锁之后,这个线程可以再次获取本对象上的锁,而其他的线程是不可以的。例如synchronized 和 ReentrantLock 都是可重入锁。
不可重入锁:当一个线程获取锁对象之后,必须等待该线程释放锁才可以获取其他对象的锁,否则会所阻塞。例如Lock锁,都是不可重入锁
2.可重入锁意义
防止锁阻塞
3.不可重入锁和ReentrantLock以及Synchronzied的对比
设计了一个不可重入锁,加锁之前判断有所等待,没有锁加锁
public class Lock{
private boolean isLocked = false;
public synchronized void lock() throws InterruptedException{
//判断当前对象是否加锁
while(isLocked){
//加锁等待
wait();
}
isLocked = true;
}
public synchronized void unlock(){
isLocked = false;
//释放当前对象的锁 唤醒任意一个 等待的线程
notify();
}
}
测试一下该lock锁
public class LockTest {
Lock lock = new Lock();
public void methodA() {
try {
lock.lock();
System.out.println("methodA加锁");
methodB();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
System.out.println("methodA释放锁");
}
}
public synchronized void methodB() {
try {
lock.lock();
System.out.println("methodA加锁");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
System.out.println("methodA释放锁");
}
}
结果:出现了死锁
测试一下该Synchronzied锁
public class LockTest_Syn {
public synchronized void methodA() {
System.out.println("methodA加锁");
methodB();
System.out.println("methodA释放锁");
}
public synchronized void methodB() {
System.out.println("methodB加锁");
System.out.println("methodB释放锁");
}
}
3.为什么Synchronized可以实现可重入锁,可重入锁又是怎样实现的呢?
比如A方法是个原子性操作,但它有需要调用B方法的原子性操作,他们还争抢的是同一个临界资源,因此需要同一把锁来加锁(ps:争抢同一临界资源的实质就是对同一把锁的争抢)
针对此情况,就有了可重入锁的概念:
public class Lock1 {
boolean isLocked = false;
Thread lockedBy = null;
int lockedCount = 0;
volatile Integer threadCount = 1;
public synchronized void lock() throws InterruptedException {
Thread thread = Thread.currentThread();
Thread.currentThread().setName("线程" + threadCount);
threadCount++;
while (isLocked && lockedBy != thread) {
System.out.println(Thread.currentThread() + "线程被等待");
wait();
}
isLocked = true;
lockedCount++;
lockedBy = thread;
System.out.println(Thread.currentThread() + "线程被加锁,第" + lockedCount + "层锁");
}
public synchronized void unlock() {
if (Thread.currentThread() == this.lockedBy) {
int flage = lockedCount--;
if (lockedCount == 0) {
isLocked = false;
notify();
System.out.println(Thread.currentThread() + "线程被解锁,第" + flage + "层锁");
}
}
}
ic static void main(String[] args) throws Exception {
Lock1 lock = new Lock1();
int count = 0;
while (count < 4) {
count++;
new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lock();
Thread.sleep(1000);
} catch (InterruptedException e) {
} finally {
lock.unlock();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lock();
Thread.sleep(1000);
} catch (InterruptedException e) {
} finally {
lock.unlock();
}
}
}).start();
}
}
}
测试结果:
可以看见代码的核心概念是:
首先解释lockedBy:顾名思义,临界资源被哪个线程锁住了。
加锁时,先获取当前线程。(识别谁需要锁)
Thread thread = Thread.currentThread();
判断:当临界资源已被锁上,但当前请求锁的线程又不是之前锁上临界资源的线程。那么当前请求锁的线程需要等待。
while(isLocked && lockedBy != thread){
wait();
}
只有当前线程满足:
1.isLocked=false ,第一次有线程进入。
2.当前请求锁的线程就是现在正在使用锁的线程。
当调用unlock() 方法若当前访问的线程就是,正在使用的线程,并且锁层数减一等于0,那么释放该锁。并唤醒随机一个等待线程
if (Thread.currentThread() == this.lockedBy) {
int flage = lockedCount--;
if (lockedCount == 0) {
isLocked = false;
notify();
}
}