• 自定义自旋锁


    什么是自旋锁

    自旋锁的定义:

              当一个线程尝试去获取某一把锁的时候,如果这个锁此时已经被别人获取(占用),那么此线程就无法获取到这把锁,该线程将会等待,间隔一段时间后会再次尝试获取。这种采用循环加锁 -> 等待的机制被称为自旋锁(spinlock)

    file

    自旋锁的原理

    自旋锁的原理比较简单,如果持有锁的线程能在短时间内释放锁资源,那么那些等待竞争锁的线程就不需要做内核态和用户态之间的切换进入阻塞状态,它们只需要等一等(自旋),等到持有锁的线程释放锁之后即可获取,这样就避免了用户进程和内核切换的消耗。

    因为自旋锁避免了操作系统进程调度和线程切换,所以自旋锁通常适用在时间比较短的情况下。由于这个原因,操作系统的内核经常使用自旋锁。但是,如果长时间上锁的话,自旋锁会非常耗费性能,它阻止了其他线程的运行和调度。线程持有锁的时间越长,则持有该锁的线程将被 OS(Operating System) 调度程序中断的风险越大。如果发生中断情况,那么其他线程将保持旋转状态(反复尝试获取锁),而持有该锁的线程并不打算释放锁,这样导致的是结果是无限期推迟,直到持有锁的线程可以完成并释放它为止。

    解决上面这种情况一个很好的方式是给自旋锁设定一个自旋时间,等时间一到立即释放自旋锁。自旋锁的目的是占着CPU资源不进行释放,等到获取锁立即进行处理。但是如何去选择自旋时间呢?如果自旋执行时间太长,会有大量的线程处于自旋状态占用 CPU 资源,进而会影响整体系统的性能。因此自旋的周期选的额外重要!JDK在1.6 引入了适应性自旋锁,适应性自旋锁意味着自旋时间不是固定的了,而是由前一次在同一个锁上的自旋时间以及锁拥有的状态来决定,基本认为一个线程上下文切换的时间是最佳的一个时间。

     

    自定义自旋锁:

    package com.spinlock.spinlock.demo;
    
    import java.util.concurrent.atomic.AtomicReference;
    
    public class SpinlockDemo {
        //自旋锁
    
        AtomicReference<Thread> atomicReference = new AtomicReference<>();
    
        //加锁
        public void myLock() {
            Thread thread = Thread.currentThread();
            System.out.println("当前线程为====lock==" + Thread.currentThread().getName());
            while (!atomicReference.compareAndSet(null, thread)) {
    
            }
        }
    
    
        //解锁
        public void myUnLock() {
            Thread thread = Thread.currentThread();
            System.out.println("当前线程为===unlock===" + Thread.currentThread().getName());
            atomicReference.compareAndSet(thread, null);
        }
    }

    测试:

    package com.spinlock.spinlock.demo;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class TestSpinLock {
        public static void main(String[] args) throws InterruptedException {
    //        ReentrantLock reentrantLock = new ReentrantLock();
    
            SpinlockDemo spinlockDemo = new SpinlockDemo();
            new Thread(() -> {
                spinlockDemo.myLock();
                try {
                    TimeUnit.SECONDS.sleep(3);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    spinlockDemo.myUnLock();
                }
    
            }, "T1").start();
    
             TimeUnit.SECONDS.sleep(1);
    
            new Thread(() -> {
                spinlockDemo.myLock();
                try {
                    TimeUnit.SECONDS.sleep(3);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    spinlockDemo.myUnLock();
                }
    
            }, "T2").start();
    
        }
    
    
    }

    结果:

  • 相关阅读:
    log4j/log4e的使用
    数据库主键不应该具有任何业务意义
    孔雀森林,何时开屏
    spring + hibernate
    JAVA的运行时类型识别(RTTI)
    开年感想,2005年总结
    iphone真机(越狱)通讯录导入进模拟器
    xcode中工程引用设置
    UIButton setImage 图片大小选择
    加密技术资源
  • 原文地址:https://www.cnblogs.com/leeego-123/p/12661636.html
Copyright © 2020-2023  润新知