10,使用CAS和AQS实现自定义锁

烟雨 5年前 (2021-06-13) 阅读数 405 #Java并发
文章标签 并发Java

java并发编程中,锁无处不在。java的容器框架中,也提供了满足各种场景的锁。但是,有一个共性就是,他们都是基于AbstractQueuedSynchronizer(AQS)。

下面,使用CAS和AQS实现一个自己的锁!

自定义独占锁只需要定义一个非公开的内部类继承AbstractQueuedSynchronizer类。AQS提供了一个模板,根据模板,重写方法即可(看见Shared结尾的方法,属于共享模式)。
acquire(int arg) 
以独占模式获取对象,忽略中断。
acquireShared(int arg)
以共享模式获取对象,忽略中断。
tryAcquire(arg)
试图在独占模式下获取对象状态。
tryAcquireShared(int arg) 
试图在共享模式下获取对象状态。
release(int arg)
以独占模式释放对象。
releaseShared(int arg) 
以共享模式释放对象。
public class MyLock implements Lock {
    /**
     * 默认信号量
     */
    private static final int DEF_STATE = 1;

    private class Sync extends AbstractQueuedSynchronizer{
        //获取锁(独占模式)
        @Override
        protected boolean tryAcquire(int arg) {
            int state = getState();
            if(state == 0){
                //能够获取到锁,通过CAS改变state的值
                if(compareAndSetState(0, arg)){
                    //设置当前线程占有资源
                    setExclusiveOwnerThread(Thread.currentThread());
                    return true;
                    //判断是否是当前线程,如果是,则重入一次(重入性)
            } else if(getExclusiveOwnerThread()==Thread.currentThread()){
                    setState(state + arg);
                    return true;
                }
            }
            return false;
        }

        //释放锁(独占模式)
        @Override
        protected boolean tryRelease(int arg) {
            int state = getState() - arg;
            //判断释放后是否为0
            if (state == 0) {
                //设置新的state=0
                setState(state);
                //清除锁的持有线程标记
                setExclusiveOwnerThread(null);
                return true;
            }
            //由于是独占模式,每次修改setState,只会有一个线程修改state,不存在多线程安全问题
            setState(state);
            return false;
        }

        public Condition newConditionObjecct(){
            return new ConditionObject();
        }
    }

    private Sync sync = new Sync();

    /**
     * 获取锁
     */
    @Override
    public void lock() {
        sync.acquire(DEF_STATE);
    }

    /**
     * 中断的方式去获取锁
     * @throws InterruptedException
     */
    @Override
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(DEF_STATE);
    }

    /**
     * 尝试去获取锁
     * @return
     */
    @Override
    public boolean tryLock() {
        return sync.tryAcquire(DEF_STATE);
    }

    /**
     * 带有超时时间的尝试去获取锁
     * @param time 自旋超时时间
     * @param unit 时间帮助类
     * @return
     * @throws InterruptedException 中断异常
     */
    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return sync.tryAcquireNanos(DEF_STATE, unit.toNanos(time));
    }

    /**
     * 释放锁
     */
    @Override
    public void unlock() {
        //调用咱们自定义的tryRelease()方法
        sync.release(DEF_STATE);
    }

    /**
     * 获取条件对象
     * @return
     */
    @Override
    public Condition newCondition() {
        return sync.newConditionObjecct();
    }
}

使用自定义的MyLock锁

public class MyLockDemo01 {
    public static int m = 0;

    private static MyLock lock = new MyLock();

    public int addM(){
        try {
            lock.lock();
            return m++;
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args){
        MyLockDemo01 demo = new MyLockDemo01();
        //初始化10个线程
        Thread[] threads = new Thread[10];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(() -> {
                System.out.println("m:" + demo.addM());
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            threads[i].start();
        }
    }
}

image.png

版权声明

非特殊说明,本文由Zender原创或收集发布,欢迎转载。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

作者文章
热门