zoukankan      html  css  js  c++  java
  • 自旋锁浅析

      自旋锁的洋名叫spin lock,是一种比较有个性的锁,因为它站在传统的互斥锁的对立面。如果并发时,互斥锁的做法是让线程阻塞,但自旋锁却不这么做,而是原地打转,不停的去抢锁,抢不到誓不罢休。简而言之,互斥锁是重量级(悲观)锁,自旋锁是轻量级(乐观)锁。自旋锁使用场景是:1、多核处理器,2、线程等待锁的时间很短,短到比线程两次上下文切换时间还少,说白了就是锁里操作的事情很简单。

      如何实现自旋锁呢?惟有CAS。何谓CAS?它的洋名叫Compare And Swap,简单来说就是比较并交换。该算法涉及三个数:内存值V,旧的预期值A,新的预期值B。当且仅当旧的预期值A和内存值V相同时,将内存值改为B,否则什么也不做。

      CAS 是实现自旋锁的基础(也是实现乐观锁的基础),CAS 利用 CPU 指令保证了操作的原子性,以达到锁的效果。先看个JDK实例:

    public class CountDownLatch {
        /**
         * Synchronization control For CountDownLatch.
         * Uses AQS state to represent count.
         */
        private static final class Sync extends AbstractQueuedSynchronizer {
            private static final long serialVersionUID = 4982264981922014374L;
    
            Sync(int count) {
                setState(count);
            }
    
            int getCount() {
                return getState();
            }
    
            protected int tryAcquireShared(int acquires) {
                return (getState() == 0) ? 1 : -1;
            }
    
            protected boolean tryReleaseShared(int releases) {
                // Decrement count; signal when transition to zero
                for (;;) {
                    int c = getState();
                    if (c == 0)
                        return false;
                    int nextc = c-1;
                    if (compareAndSetState(c, nextc))
                        return nextc == 0;
                }
            }
        }
    
        private final Sync sync;
    
        /**
         * Constructs a {@code CountDownLatch} initialized with the given count.
         *
         * @param count the number of times {@link #countDown} must be invoked
         *        before threads can pass through {@link #await}
         * @throws IllegalArgumentException if {@code count} is negative
         */
        public CountDownLatch(int count) {
            if (count < 0) throw new IllegalArgumentException("count < 0");
            this.sync = new Sync(count);
        }
    
        /**
         * Causes the current thread to wait until the latch has counted down to
         * zero, unless the thread is {@linkplain Thread#interrupt interrupted}.
         *
         * <p>If the current count is zero then this method returns immediately.
         *
         * <p>If the current count is greater than zero then the current
         * thread becomes disabled for thread scheduling purposes and lies
         * dormant until one of two things happen:
         * <ul>
         * <li>The count reaches zero due to invocations of the
         * {@link #countDown} method; or
         * <li>Some other thread {@linkplain Thread#interrupt interrupts}
         * the current thread.
         * </ul>
         *
         * <p>If the current thread:
         * <ul>
         * <li>has its interrupted status set on entry to this method; or
         * <li>is {@linkplain Thread#interrupt interrupted} while waiting,
         * </ul>
         * then {@link InterruptedException} is thrown and the current thread's
         * interrupted status is cleared.
         *
         * @throws InterruptedException if the current thread is interrupted
         *         while waiting
         */
        public void await() throws InterruptedException {
            sync.acquireSharedInterruptibly(1);
        }
    
        /**
         * Causes the current thread to wait until the latch has counted down to
         * zero, unless the thread is {@linkplain Thread#interrupt interrupted},
         * or the specified waiting time elapses.
         *
         * <p>If the current count is zero then this method returns immediately
         * with the value {@code true}.
         *
         * <p>If the current count is greater than zero then the current
         * thread becomes disabled for thread scheduling purposes and lies
         * dormant until one of three things happen:
         * <ul>
         * <li>The count reaches zero due to invocations of the
         * {@link #countDown} method; or
         * <li>Some other thread {@linkplain Thread#interrupt interrupts}
         * the current thread; or
         * <li>The specified waiting time elapses.
         * </ul>
         *
         * <p>If the count reaches zero then the method returns with the
         * value {@code true}.
         *
         * <p>If the current thread:
         * <ul>
         * <li>has its interrupted status set on entry to this method; or
         * <li>is {@linkplain Thread#interrupt interrupted} while waiting,
         * </ul>
         * then {@link InterruptedException} is thrown and the current thread's
         * interrupted status is cleared.
         *
         * <p>If the specified waiting time elapses then the value {@code false}
         * is returned.  If the time is less than or equal to zero, the method
         * will not wait at all.
         *
         * @param timeout the maximum time to wait
         * @param unit the time unit of the {@code timeout} argument
         * @return {@code true} if the count reached zero and {@code false}
         *         if the waiting time elapsed before the count reached zero
         * @throws InterruptedException if the current thread is interrupted
         *         while waiting
         */
        public boolean await(long timeout, TimeUnit unit)
            throws InterruptedException {
            return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
        }
    
        /**
         * Decrements the count of the latch, releasing all waiting threads if
         * the count reaches zero.
         *
         * <p>If the current count is greater than zero then it is decremented.
         * If the new count is zero then all waiting threads are re-enabled for
         * thread scheduling purposes.
         *
         * <p>If the current count equals zero then nothing happens.
         */
        public void countDown() {
            sync.releaseShared(1);
        }
    
        /**
         * Returns the current count.
         *
         * <p>This method is typically used for debugging and testing purposes.
         *
         * @return the current count
         */
        public long getCount() {
            return sync.getCount();
        }
    
        /**
         * Returns a string identifying this latch, as well as its state.
         * The state, in brackets, includes the String {@code "Count ="}
         * followed by the current count.
         *
         * @return a string identifying this latch, as well as its state
         */
        public String toString() {
            return super.toString() + "[Count = " + sync.getCount() + "]";
        }
    }

      看上面标红那里,就是自旋锁实现的关键:1、无限循环;2、CAS。接下来再看自旋锁的实现与应用场景:

    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;
    
    /**
     * 自旋锁
     *
     * @author wulinfeng
     * @version C10 2018年12月21日
     * @since SDP V300R003C10
     */
    public class SpinLock implements Lock
    {
        // 利用AtomicBoolean来调用CAS,ab初始(内存)值是false
        private AtomicBoolean ab = new AtomicBoolean(false);
        
        @Override
        public void lock()
        {
            /*
             * getAndSet将ab设置为true,并返回ab之前(内存)的值。 
             * 因为ab的初始(内存)值就是false,所以第一个线程不会进入循环,也就是说它抢到了锁
             * 而后面的线程来的时候,内存值已经是true,将进入循环自旋
             */
            while (ab.getAndSet(true))
            {
            }
            
        }
        
        @Override
        public void unlock()
        {
            // 将内存值重新设置为false
            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;
        }
        
    }

      测试代码:

    package com.wulinfeng.test.testpilling;
    
    import java.util.concurrent.CountDownLatch;
    
    import org.junit.After;
    import org.junit.Before;
    import org.junit.Test;
    
    import com.wulinfeng.test.testpilling.util.SpinLock;
    
    import junit.framework.TestCase;
    
    public class SpinLockTest
    {
        // 开始时间
        private long startTime = 0L;
        
        // 计数器
        private int count = 0;
        
        // 让Junit支持多线程,10个线程就先初始化10
        private CountDownLatch latch = new CountDownLatch(10);
        
        @Before
        public void before()
        {
            startTime = System.currentTimeMillis();
        }
        
        @After
        public void after()
        {
            System.out.printf("count值:%d, 耗时:%d毫秒.
    ", count, System.currentTimeMillis() - startTime);
        }
        
        @Test
        public void testSpinLock()
        {
            // 初始化自旋锁
            SpinLock sl = new SpinLock();
            
            for (int i = 0; i < 10; i++)
            {
                new Thread(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        for (int j = 0; j < 1000; j++)
                        {
                   // 加锁
                           sl.lock();
    // 自增 count++;

                           // 解锁
                           sl.unlock();
    }
    // 一个线程执行完了就减1,10个线程执行完了就变成0,执行主线程 latch.countDown(); } }).start(); } // 主线程等待 try { latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } TestCase.assertEquals(count, 10000); } }

      输出结果:

    count值:10000, 耗时:9毫秒.
  • 相关阅读:
    75. Sort Colors
    101. Symmetric Tree
    121. Best Time to Buy and Sell Stock
    136. Single Number
    104. Maximum Depth of Binary Tree
    70. Climbing Stairs
    64. Minimum Path Sum
    62. Unique Paths
    css知识点3
    css知识点2
  • 原文地址:https://www.cnblogs.com/wuxun1997/p/10152178.html
Copyright © 2011-2022 走看看