• 可重入的自旋锁


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

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

        @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毫秒.
  • 相关阅读:
    (zt)在PHP中使用全局变量
    (zt)Flash与C++交互
    (zt)关于Flash Socket通信的安全策略问题的一点心得
    (zt)svn 随服务器启动
    使用InstallShield安装和卸载SQL Server数据库(利用sql脚本)
    异常查看部分代码
    VC为控件添加背景
    深入分析MFC文档视图结构
    VC数据库编程概述
    (转)WEB程序打包详解:(连接SQL2005数据库,修改配置文件,建立虚拟目录)
  • 原文地址:https://www.cnblogs.com/wuxun1997/p/10169595.html
Copyright © 2020-2023  润新知