AQS独占式获取同步状态和释放同步状态(源码阅读笔记)

1.Lock接口的实现基本都是通过聚合了一个同步器的子类来完成线程访问控制的,例如ReentrantLock的实现

abstract static class Sync extends AbstractQueuedSynchronizer
static final class NonfairSync extends Sync
static final class FairSync extends Sync
复制代码

2.公平锁和非公平锁的实现分别依赖FairSync和NonfairSync重写了AQS的一些方法,以及调用了AQS的模板方法来独占式获取同步状态和独占式释放同步状态

//非公平获取同步状态,调用了AQS的模板方法acquire(1);
final void lock() {
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}
//公平获取同步状态,调用了AQS的模板方法acquire(1);
final void lock() {
    acquire(1);
}
复制代码

3.AQS的模板方法acquire代码实现

//独占式获取同步状态
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
tryAcquire(arg):如果尝试获取同步状态失败,就调用addWaiter(Node node)方法将当前线程包装为一个node节点插入同步队列尾部。如果追加成功则自旋的检查node的前驱是不是hade节点,如果是hade节点就尝试获取同步状态.  
复制代码

3.1 addWaiter()方法:

    /**将当前线程包装成一个node,判断尾节点是不是null,如果不是null则将尾节点赋值给node的前驱节点然后cas设置node为尾节点,如果设置成功则将node指向原来尾巴节点的后继,并返回node*/
    
    private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }
    
    /**如果尾节点是null,说明头节点也是null,就循环cas设置一个新的node为头节点,然后将新的头节点赋值给尾节点(头尾是一个节点)循环下次进入的时候尾节点就不是null了,就将当前线程包装的node,循环cas设置为尾节点,并且和新new的头节点管理*/
    
    private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }
    总结上面两个方法就是先cas设置一次尾节点 如果成功则返回node,如果失败则循环的设置 直到设置成功才返回
复制代码

3.2acquireQueued()方法:

/** 循环判断node的前驱节点是不是头节点,如果是头节点就尝试获取同步状态,如果不是头节点或者获取同步状态失败则线程
进入等待状态直到node是头节点并获取同步状态成功-则将node设置为头节点然后将原来的头节点从链表断开,然后从方法返回 
shouldParkAfterFailedAcquire,parkAndCheckInterrupt,cancelAcquire 这三个方法暂时还没看

final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
复制代码

4.简单自定义同步组件(要求在同一时刻只可以有一个线程可以获得同步状态)

/**
 * @author qinc
 * @version V1.0
 * @Description: Lock接口的实现基本都是通过聚合了一个同步器的子类来完成线程访问控制的
 * @Date 2019/4/4 11:51
 */
public class MonopolyLock implements Lock {
    private static class Sync extends AbstractQueuedSynchronizer{
        //重新获取同步状态方法tryAcquire
        @Override
        protected boolean tryAcquire(int arg) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c==0){//可以获取同步状态
                if (compareAndSetState(c,1)){
                    setExclusiveOwnerThread(current);
                    System.out.println("独占获取同步状态成功!");
                    return true;
                };
            }
           return false;
        }
        
        //重写释放同步状态方法tryRelease。独占锁,只有获得了锁才可以释放所以直接不需要cas直接setState就可以
        @Override
        protected boolean tryRelease(int arg) {
            int c = getState();
            if (c==1){
                setState(0);
                setExclusiveOwnerThread(null);
                System.out.println("独占释放同步状态成功!");
                return  true;
            }
           return  false;
        }
    }

    Sync sync = new Sync();
	
    @Override
    public void lock() {
        //独占式获取同步状态(调用AQS的模板方法)
        sync.acquire(1);
    }

    @Override
    public void unlock() {
        //独占是释放同步状态(调用AQS的模板方法)
        sync.release(1);
    }


}
测试方法:
/**
 * @Description:  测试自定义同步组件
 * @author        qinc20
 * @version       V1.0
 * @Date          2019/4/4 12:54
 */
public class MonopolyLockTest {

    public static void main(String args[]){

        ExecutorService executorService = Executors.newFixedThreadPool(20);

        MonopolyLock lock= new MonopolyLock();
        for (int i=0;i<20;i++){
            executorService.submit(new Task(lock));
        }
        executorService.shutdown();

    }
}

class Task implements Runnable{
    MonopolyLock lock;
    Task(MonopolyLock lock){
        this.lock=lock;
    }
    @Override
    public void run() {
        while (true){
            lock.lock();
            try {
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName()+" do SomeThing!!");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }

        }
    }
}

执行结果:
复制代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值