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毫秒.
  • 相关阅读:
    阿里P8架构师谈:阿里双11秒杀系统如何设计?
    秒杀系统设计的知识点
    秒杀系统架构优化思路
    秒杀系统解决方案
    Entity Framework Code First (七)空间数据类型 Spatial Data Types
    Entity Framework Code First (六)存储过程
    Entity Framework Code First (五)Fluent API
    Entity Framework Code First (四)Fluent API
    Entity Framework Code First (三)Data Annotations
    Entity Framework Code First (二)Custom Conventions
  • 原文地址:https://www.cnblogs.com/wuxun1997/p/10169595.html
Copyright © 2011-2022 走看看