Appearance
前置知识
wait
:让线程进入等待状态,直到notify
或notifyAll
通知才会唤醒,某线程调用此方法会释放锁;notify
:唤醒在对象监视器上等待的单个线程,选择是任意的notifyAll
:唤醒全部线程- 可重入锁:也叫递归锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁(注意一个锁只能被一个线程获取到)
- 不可重入锁:当前线程执行某个方法已经获取了该锁,那么在方法中尝试再次获取锁时,就会获取不到被阻塞
不可重入锁简单实现
java
public class UnreentrantLock {
// false代表解锁状态,true代表加锁状态
private boolean isLocked = false;
/** 加锁 */
public synchronized void lock() throws InterruptedException {
while (isLocked) {
wait(); // 获取不到锁进入等待状态
}
isLocked = true; // 加锁
}
/** 解锁 */
public synchronized void unlock() {
isLocked = false;
notify(); //唤醒对象锁池里面的一个线程
}
}
可重入式锁简单实现
java
public class ReentrantLock {
/** false代表解锁状态,true代表加锁状态 */
private boolean isLocked = false;
/** 用于记录是不是重入的线程 */
private Thread lockedOwner = null;
/** 累计加锁次数,加锁一次累加1,解锁一次减少1 */
private int lockedCount = 0;
/** 加锁 */
public synchronized void lock() throws InterruptedException {
// 1.获取当前正在执行的线程
Thread thread = Thread.currentThread();
// 2.如果不是同个线程获取锁,则进入等待
while (isLocked && lockedOwner != thread) {
wait(); // 获取不到锁进入等待状态
}
isLocked = true;
lockedOwner = thread;
lockedCount++;
}
/** 解锁 */
public synchronized void unlock() {
Thread thread = Thread.currentThread();
//线程A加的锁,只能由线程A解锁,其他线程B不能解锁
if (thread == this.lockedOwner) {
lockedCount--;
// 当所有锁都解除锁定才释放锁
if (lockedCount == 0) {
isLocked = false;
lockedOwner = null;
notify(); //唤醒对象锁池里面的一个线程
}
}
}
}
测试
如图所示调用methodA方法获取到锁后,调用methodB方法,methodB方法会尝试获取锁,如果是可重入式锁就能获取到,如果是不可重入锁就无法获取到
java
public class Main {
// private final UnreentrantLock myLock = new UnreentrantLock(); // 使用不可重式入锁会发生死锁
private final ReentrantLock myLock = new ReentrantLock(); // 使用可重入式锁就不会发送此问题
//加锁建议在try里面,解锁建议在finally
public void methodA() {
try {
myLock.lock();
System.out.println("methodA方法被调用");
methodB();
} catch (InterruptedException e) {
e.fillInStackTrace();
} finally {
myLock.unlock();
}
}
public void methodB() {
try {
myLock.lock();
System.out.println("methodB方法被调用");
} catch (InterruptedException e) {
e.fillInStackTrace();
} finally {
myLock.unlock();
}
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Main().methodA(); //演示的是同个线程
}
}
}