zoukankan      html  css  js  c++  java
  • 【Java】手动模拟实现一个AQS

    • 什么是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);
    • 图解一下上述代码的过程:

  • 相关阅读:
    eclipse
    ORA00904:标识符无效,preparedstatement
    mysql 创建用户
    web 默认servlet
    https tomat
    gzip
    sftp 上传文件
    jquery dwrutil confilit
    xmlbeans读写xml文件
    敏捷开发“松结对编程”实践大型团队篇
  • 原文地址:https://www.cnblogs.com/boluopabo/p/12985935.html
Copyright © 2011-2022 走看看