之前我们粗浅的介绍了自旋锁,这次主要介绍它的变种。
首先是可重入自旋锁。参照之前的实现代码,我们可以了解到,当一个线程第一次已经获取到了自旋锁,如果在锁释放之前又一次重新获取该锁,第二次就不能成功获取到。看例子:
@Test public void testNotReentrant() { // 初始化自旋锁 SpinLock sl = new SpinLock(); // 第一次获取锁 sl.lock(); System.out.println("我来了."); // 第二次获取锁 sl.lock(); System.out.println("我又来了."); sl.unlock(); sl.unlock(); }
输出结果只有"我来了.",然后程序就卡死了。要让自旋锁支持可重入,其实也很简单,加入一个计数器而已。看实例:
新增一个自旋锁的实现类:
package com.wulinfeng.test.testpilling.util; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; public class ReentrantSpinLock implements Lock { // 利用AtomicBoolean来调用CAS,ab初始(内存)值是false private AtomicBoolean ab = new AtomicBoolean(false); private int lockCount = 0; @Override public void lock() { // 获取内存值,若已取到锁(初始值为false,当内存值也为false说明取到锁了),则计数器累加、退出方法 if (!ab.get()) { lockCount++; return; } // 取不到锁,继续转啊转 while (ab.getAndSet(true)) { } } @Override public void unlock() { // 获取内存值,若已取到锁(初始值为false,当内存值也为false说明取到锁了),则计数器自减 if (!ab.get()) { if (lockCount > 0) { lockCount--; } else { // 只有计数器为0才能证明所有自旋锁已释放,这时才能真正放开锁 ab.set(false); } } } @Override public void lockInterruptibly() throws InterruptedException { // TODO Auto-generated method stub } @Override public boolean tryLock() { // TODO Auto-generated method stub return false; } @Override public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { // TODO Auto-generated method stub return false; } @Override public Condition newCondition() { // TODO Auto-generated method stub return null; } }
测试方法:
@Test public void testReentrant() { // 初始化自旋锁 ReentrantSpinLock rsl = new ReentrantSpinLock(); // 第一次获取锁 rsl.lock(); System.out.println("我来了."); // 第二次获取锁 rsl.lock(); System.out.println("我又来了."); rsl.unlock(); rsl.unlock(); }
这次输出结果对了,先打印"我来了."再打印"我又来了.",也不卡死了。