【Java】手动模拟实现一个AQS

…衆ロ難τιáo~ 提交于 2020-08-17 16:44:10
  • 什么是AQS?

synchronized(基于底层C++,语言实现的同步机制)

Aqs同步器(Java实现)

 

  • 【Unsafe】魔法类

绕过虚拟机,直接操作底层的内存

 

  • 话不多说,我们手动模拟一个AQS:

1》锁对象:

package com.example.demo.thread.current;

import com.example.demo.util.UnsafeInstance;
import sun.misc.Unsafe;

import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.locks.LockSupport;

/**
 * 公平锁
 *
 * @author Code Farmer
 * @date 2020/5/28 22:47
 */
public class AqsLock {

    /**
     * 当前加锁状态,记录加锁的次数
     */
    private volatile int state = 0;

    /**
     * 当前持有锁的线程
     */
    private Thread lockHolder;

    private ConcurrentLinkedQueue<Thread> waiters = new ConcurrentLinkedQueue<>();//基于CAS保证入队出队安全

    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
    }

    public Thread getLockHolder() {
        return lockHolder;
    }

    public void setLockHolder(Thread lockHolder) {
        this.lockHolder = lockHolder;
    }

    /**
     * 尝试获取锁
     *
     * @return
     */
    private boolean acquire() {
        Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {//同步器还没有加锁
//            if (waiters.size() == 0 || compareAndSwapInt(0, 1)) { //如果这么写,可能会导致有某一个线程获取到锁,然后poll出后,队列为空,导致下一个线程又可以直接获取到锁
            if ((waiters.size() == 0 || current == waiters.peek()) && compareAndSwapInt(0, 1)) {
                this.setLockHolder(current);
                return true;
            }
        }
        return false;
    }

    /**
     * 加锁
     */
    public void lock() {
        if (acquire()) {//加锁成功
            return;
        } else {
            Thread current = Thread.currentThread();
            waiters.add(current);//保存对线程的引用
            for (; ; ) {
                //让出cpu使用权
                //Thread.yield();//会持续浪费CPU
                //Thread.sleep(1000)  //为什么不使用sleep?虽然使用sleep会减少cpu的使用,但是1000ms时间可能会发生这样事情,200ms的时候可能上一个线程释放锁,然后其他线程要阻塞800ms,如果是一个用户业务,那个用户体验会非常差
                //Thread.sleep(1); //1ms会造成频繁切换上下文而造成cpu浪费
                if ((current == waiters.peek()) && acquire()) {
                    waiters.poll();//唤醒后,把自身从队列中移出
                    return;
                }
                //阻塞当前线程,释放cpu资源
                LockSupport.park(current);
            }
        }
    }

    /**
     * 解锁
     */
    public void unlock() {
        if (Thread.currentThread() != lockHolder) {
            throw new RuntimeException("lock holder is not current thread");
        }
        int state = getState();
        if (compareAndSwapInt(state, 0)) {
            setLockHolder(null);
            Thread first = waiters.peek();
            if (first != null) {
                LockSupport.unpark(first);//由于唤醒线程是随机唤醒,我们不能保证唤醒的是哪一个线程,所以需要在获取锁时判断是不是等于队列第一个线程
            }
        }
    }

    public final boolean compareAndSwapInt(int expect, int update) {
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

    private static final Unsafe unsafe = UnsafeInstance.reflectGetUnsafe();

    private static long stateOffset;

    /**
     * 计算state变量偏移量
     */
    static {
        try {
            stateOffset = unsafe.objectFieldOffset(AqsLock.class.getDeclaredField("state"));
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

}

 

 

 2》Unsafe实例对象:

package com.example.demo.util;

import sun.misc.Unsafe;

import java.lang.reflect.Field;

/**
 * @author Code Farmer
 * @date 2020/5/28 23:42
 */
public class UnsafeInstance {

    public static Unsafe reflectGetUnsafe() {
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            return (Unsafe) field.get(null);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

}

 

3》业务中实现加锁:

package com.example.demo.service.impl;

import com.example.demo.service.AqsDemoService;
import com.example.demo.thread.current.AqsLock;

/**
 * @author Code Farmer
 * @date 2020/5/28 22:54
 */
public class AqsDemoServiceImpl implements AqsDemoService {

    AqsLock aqsLock = new AqsLock();

    @Override
    public String decStockNoLock() {

        aqsLock.lock();

        //一个查询操作(略)

        //一个更新操作(略)

        aqsLock.unlock();


        return null;

    }
}

 

 

 

  • 这里阻塞和唤醒用了【Unsafe】中的【park】和【unpark】
#会把cpu缓存当中运行时数据全部清除,保存至内存(RSS)当中

LockSupport.park(current);

 

#唤醒线程

LockSupport.unpark(current);

 

 

 

  • 图解一下上述代码的过程:

 

 

 

 

 

 

 

 

 

 

 

 

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!