可重入的自旋锁

  之前我们粗浅的介绍了自旋锁(参见自旋锁浅析),这次主要介绍它的变种。

  首先是可重入自旋锁。参照之前的实现代码,我们可以了解到,当一个线程第一次已经获取到了自旋锁,如果在锁释放之前又一次重新获取该锁,第二次就不能成功获取到。看例子:

    @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.AtomicReference;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
 * 可重入自旋锁
 *
 * @author wulinfeng
 * @version C10 2018年12月25日
 * @since SDP V300R003C10
 */
public class ReentrantSpinLock implements Lock
{
    // 利用AtomicReference来调用CAS,ar初始(内存)值是null
    private AtomicReference<Thread> ar = new AtomicReference<Thread>();
    
    private int lockCount = 0;
    
    @Override
    public void lock()
    {
        Thread currentThread = Thread.currentThread();
        
        // 获取内存值,若已取到锁(初始值为null,当内存值也为null说明取到锁了),则计数器累加、退出方法
        if (currentThread == ar.get())
        {
            lockCount++;
            return;
        }
        
        // 取不到锁,继续转啊转
        while (!ar.compareAndSet(null, currentThread))
        {
        }
        
    }
    
    @Override
    public void unlock()
    {
        Thread currentThread = Thread.currentThread();
        
        // 获取内存值,若已取到锁(初始值为null,当内存值也为null说明取到锁了),则计数器自减
        if (currentThread == ar.get())
        {
            if (lockCount > 0)
            {
                lockCount--;
            }
            else
            {
                // 只有计数器为0才能证明所有自旋锁已释放,这时才能真正放开锁,重置为null
                ar.compareAndSet(currentThread, null);
            }
        }
        
    }
    
    @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();
    }

  这次输出结果对了,先打印"我来了."再打印"我又来了.",也不卡死了。这个是单线程,再看多线程:

   @Test
    public void testReentrantSpinLock()
    {
        // 初始化自旋锁
        ReentrantSpinLock rsl = new ReentrantSpinLock();
        
        for (int i = 0; i < 10; i++)
        {
            new Thread(new Runnable()
            {
                @Override
                public void run()
                {
                    for (int j = 0; j < 1000; j++)
                    {
                        // 加锁
                        rsl.lock();
                        
                        // 自增
                        count++;
                        
                        // 解锁
                        rsl.unlock();
                    }
                    
                    // 一个线程执行完了就减1,10个线程执行完了就变成0,执行主线程
                    latch.countDown();
                }
            }).start();
        }
        
        // 主线程等待
        try
        {
            latch.await();
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        
        
        TestCase.assertEquals(count, 10000);
    }

  输出:

count值:10000, 耗时:16毫秒.
原文地址:https://www.cnblogs.com/wuxun1997/p/10169595.html