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

    原创转载请注明出处:https://www.cnblogs.com/agilestyle/p/11395994.html

    CAS

    CAS算法是乐观锁的一种实现方式,CAS算法中又涉及到自旋锁。

    CAS是英文单词Compare and Swap(比较并交换),是一种有名的无锁算法。无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(Non-blocking Synchronization)。

    CAS算法涉及到三个操作数

    1.需要读写的内存值 V
    2.进行比较的值 A
    3.拟写入的新值 B

    更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改为B,否则不会执行任何操作。一般情况下是一个自旋操作,即不断的重试。

    自旋锁

    自旋锁(spinlock):是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。

    它是为实现保护共享资源而提出一种锁机制。其实,自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,”自旋”一词就是因此而得名。

    Java实现自旋锁

     1 package org.fool.hellojava.lock;  
     2   
     3 import java.util.concurrent.atomic.AtomicReference;  
     4   
     5 public class SpinLockTest {  
     6     private static SpinLock lock = new SpinLock();  
     7   
     8     public static void main(String[] args) {  
     9         new Thread(() -> {  
    10             lock.lock();  
    11   
    12             test();  
    13   
    14             lock.unlock();  
    15         }).start();  
    16   
    17         new Thread(() -> {  
    18             lock.lock();  
    19   
    20             test();  
    21   
    22             lock.unlock();  
    23         }).start();  
    24   
    25         new Thread(() -> {  
    26             lock.lock();  
    27   
    28             test();  
    29   
    30             lock.unlock();  
    31         }).start();  
    32     }  
    33   
    34     public static void test() {  
    35         System.out.println(Thread.currentThread().getName() + " invoked test...");  
    36   
    37         try {  
    38             Thread.sleep(500);  
    39         } catch (InterruptedException e) {  
    40             e.printStackTrace();  
    41         }  
    42     }  
    43   
    44     private static class SpinLock {  
    45         private AtomicReference<Thread> cas = new AtomicReference<>();  
    46   
    47         public void lock() {  
    48             Thread currentThread = Thread.currentThread();  
    49   
    50             while (!cas.compareAndSet(null, currentThread)) {  
    51                 System.out.println(Thread.currentThread().getName() + " waiting...");  
    52             }  
    53         }  
    54   
    55         public void unlock() {  
    56             Thread currentThread = Thread.currentThread();  
    57             cas.compareAndSet(currentThread, null);  
    58         }  
    59   
    60     }  
    61 }  

    lock()方法利用的CAS,当第一个线程A获取锁的时候,能够成功获取到,不会进入while循环,如果此时线程A没有释放锁,另一个线程B又来获取锁,此时由于不满足CAS,所以就会进入while循环,不断判断是否满足CAS,直到A线程调用unlock方法释放了该锁。

    自旋锁的缺点

    1.如果某个线程持有锁的时间过长,就会导致其它等待获取锁的线程进入循环等待,消耗CPU。使用不当会造成CPU使用率极高。
    2.上面Java实现的自旋锁不是公平的,即无法满足等待时间最长的线程优先获取锁。不公平的锁就会存在“线程饥饿”问题。

    自旋锁的优点

    1.自旋锁不会使线程状态发生切换,一直处于用户态,即线程一直都是active的;不会使线程进入阻塞状态,减少了不必要的上下文切换,执行速度快
    2.非自旋锁在获取不到锁的时候会进入阻塞状态,从而进入内核态,当获取到锁的时候需要从内核态恢复,需要线程上下文切换。 (线程被阻塞后便进入内核(Linux)调度状态,这个会导致系统在用户态与内核态之间来回切换,严重影响锁的性能)

    Java实现可重入自旋锁

    为了实现可重入锁,需要引入一个计数器,用来记录获取锁的线程数。

     1 package org.fool.hellojava.lock;  
     2   
     3 import java.util.concurrent.atomic.AtomicReference;  
     4   
     5 public class ReentrantSpinLockTest {  
     6   
     7     private static ReentrantSpinLock lock = new ReentrantSpinLock();  
     8   
     9     public static void main(String[] args) {  
    10         new Thread(() -> {  
    11             lock.lock();  
    12   
    13             test();  
    14   
    15             lock.unlock();  
    16         }).start();  
    17     }  
    18   
    19     public static void test() {  
    20         lock.lock();  
    21         System.out.println(Thread.currentThread().getName() + " invoked test...");  
    22   
    23         try {  
    24             Thread.sleep(5000);  
    25         } catch (InterruptedException e) {  
    26             e.printStackTrace();  
    27         }  
    28   
    29         lock.unlock();  
    30     }  
    31   
    32     private static class ReentrantSpinLock {  
    33         private AtomicReference<Thread> cas = new AtomicReference<>();  
    34         private int count;  
    35   
    36         public void lock() {  
    37             Thread currentThread = Thread.currentThread();  
    38   
    39             if (currentThread == cas.get()) {  
    40                 count++;  
    41                 return;  
    42             }  
    43   
    44             while (!cas.compareAndSet(null, currentThread)) {  
    45                 // DO nothing  
    46                 System.out.println(Thread.currentThread().getName() + " waiting...");  
    47             }  
    48         }  
    49   
    50         public void unlock() {  
    51             Thread currentThread = Thread.currentThread();  
    52             if (currentThread == cas.get()) {  
    53                 if (count > 0) {  
    54                     count--;  
    55                 } else {  
    56                     cas.compareAndSet(currentThread, null);  
    57                 }  
    58             }  
    59         }  
    60     }  
    61 }  

    自旋锁与互斥锁

    1.自旋锁与互斥锁都是为了实现保护资源共享的机制。
    2.无论是自旋锁还是互斥锁,在任意时刻,都最多只能有一个保持者。
    3.获取互斥锁的线程,如果锁已经被占用,则该线程将进入睡眠状态;获取自旋锁的线程则不会睡眠,而是一直循环等待锁释放。

    自旋锁总结

    1.自旋锁:线程获取锁的时候,如果锁被其他线程持有,则当前线程将循环等待,直到获取到锁。
    2.自旋锁等待期间,线程的状态不会改变,线程一直是用户态并且是活动的(active)。
    3.自旋锁如果持有锁的时间太长,则会导致其它等待获取锁的线程耗尽CPU。
    4.自旋锁本身无法保证公平性,同时也无法保证可重入性。
    5.基于自旋锁,可以实现具备公平性和可重入性质的锁。

     

  • 相关阅读:
    389 Directory Server RHEL 6.0以上客户端安装方法
    389 Directory Server RHEL 6.0以下客户端安装方法
    堆排序算法
    单链表倒置(递归与非递归)
    发帖水王问题及扩展
    2.5 k个最大的数
    2.7 最大公约数
    进制转化的算法问题。
    boost图库简单操作
    C++编程必备
  • 原文地址:https://www.cnblogs.com/agilestyle/p/11395994.html
Copyright © 2011-2022 走看看