Java并发编程---显示锁和AQS(二)

前言

浏览过JDK源码的,不难发现很多相关的类底层都是与AbstractQueuedSynchronized(AQS)相关的。像类ReentrantLock虽然没有直接继承了AQS,但它却有一个内部类Sync用来继承AQS的。其余相关的锁的实现也是大同小异的。下面我们简单的了解一下AQS的相关知识。

1.AQS使用的方式和其中的设计模式

许多相关的显示锁都是用一个相关的内部来继承AQS,并实现相关的流程方法(后面会详细说明)
设计模式:模板方法设计模式(在父类定义一个框架方法,它用来调用各流程方法(这些流程方法在父类中不实现)
子类具体实现流程方法,并调用父类的框架方法)

模板方法的简单实例:设计一个发送信息的抽象父类作为模板,再设计一个具体的子类去发送具体的信息

模板父类:

package 模板方法;

import java.util.Date;

/**
 * @author lenovo
 * 模板方法模式:
 * 在父类定义一个框架方法,它用来调用各流程方法(在父类中不实现)
 * 子类具体实现流程方法,并调用父类的框架方法
 *
 */
public abstract class SendCustom {

    /**
     * 前面的这几个方法都为所谓的流程方法需要子类去具体实现
     */
    public abstract void to();
    public abstract void from();
    public abstract void content();

    public void date(){
        System.out.println(new Date());
    }
    public abstract void send();

    //框架方法---模板方法,用来调用相关的流程方法
    public void sendMessage(){
        to();
        from();
        content();
        date();
        send();
    }

}

发送具体信息的子类:

package 模板方法;

/**
 * @author cgs
 */
public class SendSms extends SendCustom {
    @Override
    public void to() {
        System.out.println("Mark");
    }

    @Override
    public void from() {
        System.out.println("Bill");
    }

    @Override
    public void content() {
        System.out.println("Hello world");
    }

    @Override
    public void send() {
        System.out.println("Send sms");
    }

    public static void main(String[] args) {
        SendCustom sc=new SendSms();
        sc.sendMessage();

    }
}

2.AQS中的相关方法:
由于和锁相关,所以它分为独占式和共享式的方法。
(1)模板方法:独占式获取:acquire(), acquireInterruptibly() [尝试中断式获取] ,tryAcquireNanos() [尝试超时获取]
共享式获取:acquireShared(), acquireSharedInterruptibly(), tryAcquireSharedNanos()
独占式释放锁:release() 共享式释放锁:releaseShared()
(模板方法就像上面所讲的用来调用子类实现的流程方法)

(2)需要子类覆盖的流程方法: 独占式获取: tryAcquire() 独占式释放:tryRelease()
共享式获取:tryAcquireShared() 共享式释放:tryReleaseShared()
判断这个同步器是否处于独占模式:isHeldExclusively()

(3)同步状态 state: (state是用volatile修饰的,能保证动作的可见性)
getState: 获取当前的同步状态
setState:设置当前的同步状态 (因为可能有多个线程同时竞争,不能保证操作的原子性)
compareAndSetState: 使用CAS设置状态,保证状态设置的原子性

3.AQS中的数据结构----节点和同步队列(双向)
(1)同步队列
在这里插入图片描述

(2)Node里相关变量的源码及解释:
源码:

		/** Marker to indicate a node is waiting in shared mode */
        static final Node SHARED = new Node();
        /** Marker to indicate a node is waiting in exclusive mode */
        static final Node EXCLUSIVE = null;

        /** waitStatus value to indicate thread has cancelled */
        static final int CANCELLED =  1;
        /** waitStatus value to indicate successor's thread needs unparking */
        static final int SIGNAL    = -1;
        /** waitStatus value to indicate thread is waiting on condition */
        static final int CONDITION = -2;
        /**
         * waitStatus value to indicate the next acquireShared should
         * unconditionally propagate
         */
        static final int PROPAGATE = -3;
        volatile int waitStatus;
        volatile Node prev;
        volatile Node next;
        volatile Thread thread;
        Node nextWaiter;

 CANELED:线程等待超时或者被中断了,需要从队列中移出
 SIGNAL: 后续的节点等待状态。当前节点去通知后面的节点运行
 CONDTION: 当前节点处于等待队列
 PROPAGATE: 共享,表示状态要往后面的节点传播。
 0:表示初始的状态
 waitStatus:表示等待的状态,也就是用上面的常量进行设置
 prev: 表示队列的上一个节点
 next:表示队列的下一个节点
 thread: 表示要封装到节点里的相关信息(线程值)
 nextWaiter:表时下一个等待队列的值

(3)节点在同步队列中的增加和移出
节点加入同步队列
在这里插入图片描述
节点移出同步队列
在这里插入图片描述
独占式同步状态获取与释放
在这里插入图片描述
(4) Condition分析(AQS中有一个Condition的相关实现)
一个Condition包含一个等待队列(单向)

在这里插入图片描述
同步队列与等待队列(一个锁可以产生多个Condition)即一个同步队列和多个等待队列
在这里插入图片描述
节点在队列之间的移动
1) await 方法(只有获取锁的线程才能调用)
在这里插入图片描述
2) signal方法(有多个等待线程)
在这里插入图片描述

signal()方法与notifyAll() 的对比: 这两者都能实现等待通知范式中的通知,不过在应用场合有所不同。signal()是在使用Condition和Lock下的通知,而notifyAll()是在使用Synchronized下的通知。在Synchronized内部实现该机制的方法也是一个等待队列,不过有且仅有一个,而Condition下却有多个等待队列,所以在Synchronized使用的方法后要加上-All,因为不确定唤醒的线程是否排在对首。而Condition使用的方法后面却不用加上-All,因为每一个Condition都有一个自己独立的等待队列。

4.实现一个自己的独占锁和共享锁
a.实现一个独占锁
(1)由上面的知识积累我们知道,实现一个独占锁,我们可以定义一个内部类去继承AQS(与许多jdk源码的实现相似)
然后在这个内部类中重写子类需要覆盖的独占式的流程方法:
独占式的获取:tryAcquire() 独占式释放: tryRelease() 是否处于独占模式 isHeldExclusively()
还有别忘了返回一个Condition对象

  //state 表示获取到state=1 获取到了锁, state=0表示这个锁当前没有线程拿到
    private static class Sync extends AbstractQueuedSynchronizer{

        //是否占用
        @Override
        protected boolean isHeldExclusively(){
            return getState()==1;
        }

        @Override
        protected boolean tryAcquire(int arg) {
            //compareAndSetState(0,1) 0表示当前的锁没有线程拿到,1表示当前的锁被该线程独占
            if(compareAndSetState(0,1)){
                //设置为独占型
                //表示当前线程拿到了这个锁
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        @Override
        protected boolean tryRelease(int arg) {
            if(getState()==0){
                throw new UnsupportedOperationException();
            }
            //表示当前没有线程进行独占
            setExclusiveOwnerThread(null);
            //这里可以不用采用原子操作(因为这是独占式的锁,只有获得锁的线程才能进行释放)
            setState(0);
            return true;
        }

        Condition newCondition(){
            return new ConditionObject();
        }
    }

(2)定义一个相关类继承Lock时,实现Lock接口下的相关方法,并在这些方法中调用相关的独占式模板方法(在独占式的模板方法下会自动调用我们实现的流程方法)

private final Sync sync=new 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() {
        return sync.newCondition();
    }

(3)相关的完整代码

package 显示锁和AQS.SelfLock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.*;

/**
 * @author lenovo
 * 类说明:实现一个自己的类ReentrantLock(可重入锁)
 */
public class SelfLock implements Lock {


    //state 表示获取到state=1 获取到了锁, state=0表示这个锁当前没有线程拿到
    private static class Sync extends AbstractQueuedSynchronizer{

        //是否占用
        @Override
        protected boolean isHeldExclusively(){
            return getState()==1;
        }

        @Override
        protected boolean tryAcquire(int arg) {
            //compareAndSetState(0,1) 0表示当前的锁没有线程拿到,1表示当前的锁被该线程独占
            if(compareAndSetState(0,1)){
                //设置为独占型
                //表示当前线程拿到了这个锁
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        @Override
        protected boolean tryRelease(int arg) {
            if(getState()==0){
                throw new UnsupportedOperationException();
            }
            //表示当前没有线程进行独占
            setExclusiveOwnerThread(null);
            //这里可以不用采用原子操作(因为这是独占式的锁,只有获得锁的线程才能进行释放)
            setState(0);
            return true;
        }

        Condition newCondition(){
            return new ConditionObject();
        }
    }

    private final Sync sync=new 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() {
        return sync.newCondition();
    }
}

b. 实现一个共享锁(流程与独占锁类似)

package 显示锁和AQS.SelfLock;


import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
 * 实现一个三元共享锁
 */
public class TrinityLock implements Lock {

    private final Sync sync = new Sync(3);

    private static final class Sync extends AbstractQueuedSynchronizer{

        Sync(int count){
            if(count <= 0){
                throw new IllegalArgumentException("count must large than zero.");
            }
            setState(count);
        }

        public int tryAcquireShared(int reduceCount){
            for(;;){
                int current = getState();
                int newCount = current - reduceCount;
                if(newCount < 0 || compareAndSetState(current,newCount)){
                    return newCount;
                }
            }
        }

        public boolean tryReleaseShared(int returnCount){
            for(;;){
                int current = getState();
                int newCount = current + returnCount;
                if(compareAndSetState(current, newCount)){
                    return true;
                }
            }
        }

        final ConditionObject newCondition(){
            return new ConditionObject();
        }

    }

    @Override
    public void lock() {
        sync.acquireShared(1);
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

    @Override
    public boolean tryLock() {
        return sync.tryAcquireShared(1) >= 0;
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return sync.tryAcquireSharedNanos(1,unit.toNanos(time));
    }

    @Override
    public void unlock() {
        sync.releaseShared(1);
    }

    @Override
    public Condition newCondition() {
        return sync.newCondition();
    }
}

c. 相关的测试代码

package 显示锁和AQS.SelfLock;

import java.util.concurrent.locks.Lock;

/**
 * @author lenovo
 */
public class TestMyLock {
    public void test() throws InterruptedException {

        
        final Lock lock=new TrinityLock();

        class Worker extends Thread{
            @Override
            public void run() {
                while(true){
                    lock.lock();
                    try{
                        Thread.sleep(1000);
                        System.out.println(Thread.currentThread().getName());
                        Thread.sleep(1000);
                    }catch (Exception e){

                    } finally {
                        lock.unlock();
                    }
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        //启动10个子线程
        for(int i=0;i<10;i++){
            Worker w=new Worker();
            w.setDaemon(true);
            w.start();
        }
        //主线程隔1秒换行
        for(int i=0;i<10;i++){
            Thread.sleep(1000);
            System.out.println();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        TestMyLock t= new TestMyLock();
        t.test();
    }
}

总结:
这就是AQS中使用的相关方法以及其中重要数据结构的解读。无论是谁都很难去理解JDK的源码,我们最好的方法是通过图去了解内部的数据结构,并试着去编写自己的显示锁,这样就能很好地理解AQS和使用显示锁了。

发布了19 篇原创文章 · 获赞 2 · 访问量 416

猜你喜欢

转载自blog.csdn.net/TheWindOfSon/article/details/103897282
今日推荐