zoukankan      html  css  js  c++  java
  • 可重入的自旋锁

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

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

        @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毫秒.
  • 相关阅读:
    50种方法巧妙优化你的SQL Server数据库
    sql server数据库中选出指定范围的行的sql语句写法
    如何开启SQLSERVER数据库缓存依赖优化网站性能
    网站项目建设流程概述
    一个flash网页图片播放器
    ASP.NET中备份sqlserver数据库的方法
    ASP.NET(C#)实现一次性上传多张图片(多个文件)
    GridView使用技巧之:新增记录、GridView内数据验证、删除信息提示等
    FreeTextBox控件上传图片到指定的绝对路径的改进
    Linux环境下的32位与64位
  • 原文地址:https://www.cnblogs.com/wuxun1997/p/10169595.html
Copyright © 2011-2022 走看看