自旋锁(spinlock)的理解

1.什么是自旋锁?
是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗;缺点是循环会消耗CPU资源。
jdk中的rt.jar包的unsafe类中getAndAddInt方法就采用了自旋锁,源码如下:

 // unsafe.getAndAddInt
 public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
   }

从源码中可以看出,在更新值时,会先取出值和期望值进行比较,如果相同,则更改值,如果不相同,则进行do……while循环,直到保证取值到赋值中间没有其他线程对值进行更改时,再对值进行更改,这也保证了JMM的原子性。

2.如何自己编写一个自旋锁?
代码如下:

package Demo;


import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @ClassName:SpinLockDemo
 * @Description:自旋锁
 * @Author:legend Chan
 * @Date:2019/11/7 3:23 下午
 * @Version V1.0
 **/
public class SpinLockDemo {
    // 原子引用线程
    AtomicReference<Thread> atomicReference = new AtomicReference<>();
    public void myLock(){
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName()+"\t come in");
        // 自旋锁部分
        while (!atomicReference.compareAndSet(null,thread)){

        }
    }
    public void myUnLock(){
        Thread thread = Thread.currentThread();
        atomicReference.compareAndSet(thread,null);
        System.out.println(Thread.currentThread().getName()+"\t invoked myUnLock()");
    }
    public static void main(String[] args) throws InterruptedException {

        SpinLockDemo spinLockDemo = new SpinLockDemo();

        new Thread(()->{
            spinLockDemo.myLock();
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            spinLockDemo.myUnLock();
        },"t1").start();
        // 为了保证t1 线程先获取到锁
        TimeUnit.SECONDS.sleep(2);
        
        new Thread(()->{
            spinLockDemo.myLock();
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            spinLockDemo.myUnLock();
        },"t2").start();
    }
}

代码中模拟了t1,t2两个线程运行,t1执行myLock()后,会把原子引用由null改为当前线程,然后休眠5s,执行myUnLock()方法,把原子引用由当前线程改为null;

在t1运行2s后,t2线程启动,同样执行myLock()方法,但是它期望原子引用的值为null,才能更改,所以t2进入循环等待,要等到原子引用的值为null时,才能继续执行,也就是等t1执行myUnLock()方法后,t2才跳出while循环,继续执行,这个循环等待的过程也就是自旋锁的思想。

程序运行结果如下:

Connected to the target VM, address: '127.0.0.1:57724', transport: 'socket'
t1	 come in
t2	 come in
t1	 invoked myUnLock()
t2	 invoked myUnLock()
Disconnected from the target VM, address: '127.0.0.1:57724', transport: 'socket'

Process finished with exit code 0

你可能感兴趣的:(Java学习笔记)