假设我们现在去买水果,创建一个买水果的类,里面默认有500块。如下:
public class BuyFruit {
int money = 500;
}
然后我们设置两个买水果的方法,如下:
public class BuyFruit {
int money = 500;
public void buyApple() {
money -= 5;
}
public void buyOrange() {
money -= 4;
}
}
我们知道,这样子的加减操作并不是线程安全的(如果不了解,可以先搜索一下“i++是线程安全的吗”等类似问题),所以我们需要加锁,并且使用了ReentrantLock这个可重入锁,如下:
public class BuyFruit {
int money = 500;
ReentrantLock lock = new ReentrantLock();
public void buyApple() {
lock.lock();
money -= 5;
lock.unlock();
}
public void buyOrange() {
lock.lock();
money -= 4;
lock.unlock();
}
}
现在我们可以很好地购买苹果和橙子了,也没有并发问题存在。
水果店推出了一套新购买方案,购买水果的时候也可以购买水果盒子,水果店免费帮助包装,并且水果价钱便宜1块,另外如果只是想购买水果盒子,水果店也是可以出售的。这样我们就可以有了下面的代码:
public class BuyFruit {
int money = 500;
ReentrantLock lock = new ReentrantLock();
public void buyApple() {
lock.lock();
money -= 5;
lock.unlock();
}
public void buyOrange() {
lock.lock();
money -= 4;
lock.unlock();
}
public void buyBox() {
lock.lock();
money -= 2;
lock.unlock();
}
public void buyAppleWithBox() {
lock.lock();
buyApple();
buyBox();
money += 1;
lock.unlock();
}
public void buyOrangeWithBox() {
lock.lock();
buyOrange();
buyBox();
money += 1;
lock.unlock();
}
}
可以发现buyAppleWithBox()和buyOrangeWithBox()里面出现了锁重入。
假设锁不支持重入,那会出现什么?那么线程就会在第二次lock.lock()的时候获取不到锁而阻塞,而想要释放锁,又需要该线程执行下去,这样就死锁了,所以可重入锁的使用场景就来了。
当然,上面的需求也可以使用原子类AtomicInteger来实现,这里仅仅只是想说明一下,随着需求的增加,有时候锁的重入是难以避免的,所以支持重入是锁的一项重要功能。