09-Java多线程-5、AQS-基于AQS实现自定义锁

文章目录

    • 一、自定义锁
    • 二、代码
    • 三、测试
    • 四、输出

一、自定义锁

  • ASQ设计是为了简化并发工具的实现,里面实现了大量线程状态的维护,线程间的同步等待通知等细节,java并发的锁等很多基础组件都是基于AQS来实现的,因此可以通过AQS来自定义实现锁,在AQS类的注释上我们可以看到作者Doug Lea下面一段话,具
    体真的推荐去看看源码中的英文注释,非常多的注释对理解和使用AQS都很有帮助,读起来吃力可以直接谷歌翻译。

   Provides a framework for implementing blocking locks and related
   synchronizers (semaphores, events, etc) that rely on
   first-in-first-out (FIFO) wait queues. 
   提供一个基于FIFO等待队列的框架来实现阻塞锁和相关的同步器(semaphores, events,等)
   
   This class is designed to be a useful basis for most kinds of synchronizers that rely on a
   single atomic {@code int} value to represent state.
   这个类基于一个原子变量来表示状态,并设计成一个对于大多与同步器有用的基础组件。
   
   
   Subclasses must define the protected methods that change this state, and which
   define what that state means in terms of this object being acquired or released. 
   子类必须定义那些改变state状态的protected方法,子类应该定义对象获得或者释放states状态的含义。
   (这里不太好翻译和理解,意思就是子类需要重写对应的protected方法,在方法里面会改变states状态,我们
   需要描述出获得或者释放states的含义,,大概这么理解吧。)
   
   
   Given these, the other methods in this class carry out all queuing and blocking mechanics. 
   基于这些,AQS类里面的其他方法会实现队列和阻塞机制。

  Subclasses should be defined as non-public internal helper
  classes that are used to implement the synchronization properties
  of their enclosing class.
  子类应该定义一个非公开的内部帮助类来协助外部类实现同步属性。
  
  • 本文基于AQS实现一个互斥锁。在JDK中基于AQS实现了很多锁和同步工具类,如CountDownLatch,ReentrantLock,ReentrantReadWriteLock、Semaphore,可以参照我之前的分析文章。

二、代码

public class MyLock implements Lock {

    /**
     * 这是内部类,内部类继承ASQ,重写ASQ中获取同步状态的几个方法(因为是实现排它锁,重写阻塞模式的方法即可)
     */
    private static class Sync extends AbstractQueuedSynchronizer {

        /**
         * state 表示获取到锁 state=1 获取到了锁,state=0,表示这个锁当前没有线程拿到
         */
        @Override
        protected boolean isHeldExclusively() {
            return getState() == 1;
        }

        /**
         * 尝试获取锁,将状态从0修改为1,修改成功则将自身线程设置为当前拥有锁的线程
         */
        @Override
        protected boolean tryAcquire(int arg) {
            if (compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        /**
         * 释放锁,将状态修改为0,因为只有一个线程持有锁,因此不需要CAS,是线程安全的
         */
        @Override
        protected boolean tryRelease(int arg) {
            if (getState() == 0) {
                throw new UnsupportedOperationException();
            }
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }

        Condition newCondition() {
            return sync.newCondition();
        }
    }

    private static final Sync sync = new Sync();

    /**
     * 下面的实现Lock接口需要重写的方法,基本是就是调用内部内Sync的方法
     */
    @Override
    public void lock() {
        //加锁
        sync.acquire(1);
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
        //加锁,可响应中断
        sync.acquireInterruptibly(1);
    }

    @Override
    public boolean tryLock() {
        //尝试加锁
        return sync.tryAcquire(1);
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        //加锁,可超时
        return sync.tryAcquireNanos(1, unit.toNanos(time));
    }

    @Override
    public void unlock() {
        //解锁
        sync.release(1);
    }

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

三、测试

  • 我们定义了10个线程,所有线程共用一把锁,预期线程会同步依次执行打印。
public class MyLockTest {

    private static Lock lock = new MyLock();

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new MyThread(lock, "Thread-" + i).start();
        }
    }

    static class MyThread extends Thread {

        Lock lock;

        public MyThread(Lock lock, String name) {
            super(name);
            this.lock = lock;
        }

        @Override
        public void run() {
            lock.lock();
            try {
                System.out.println("Thread " + Thread.currentThread().getName() + " do something thing..." +  new Date());
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
}

四、输出

  • 我们看到所有的线程排队输出,每一秒输出一次,说明我们的锁机制生效了
Thread Thread-0 do something thing...Mon Apr 29 19:16:57 CST 2019
Thread Thread-1 do something thing...Mon Apr 29 19:16:58 CST 2019
Thread Thread-2 do something thing...Mon Apr 29 19:16:59 CST 2019
Thread Thread-4 do something thing...Mon Apr 29 19:17:00 CST 2019
Thread Thread-5 do something thing...Mon Apr 29 19:17:01 CST 2019
Thread Thread-6 do something thing...Mon Apr 29 19:17:02 CST 2019
Thread Thread-3 do something thing...Mon Apr 29 19:17:03 CST 2019
Thread Thread-7 do something thing...Mon Apr 29 19:17:04 CST 2019
Thread Thread-8 do something thing...Mon Apr 29 19:17:05 CST 2019
Thread Thread-9 do something thing...Mon Apr 29 19:17:06 CST 2019

你可能感兴趣的:(并发编程)