【记录】并发编程 - 学习日志(十一)

8.2 AQS 原理 

特点  

1. 用 state 属性来表示资源的状态(分独占模式和共享模式),控制如何获取锁和释放锁 

        getState - 获取 state 状态

        setState - 设置 state 状态 

        compareAndSetState - cas 机制设置 state 状态 

        独占模式是只有一个线程能够访问资源,而共享模式可以允许多个线程访问资源 

2. 提供了基于 FIFO 的等待队列,类似于 Monitor 的 EntryList 

3.  条件变量来实现等待、唤醒机制,支持多个条件变量,类似于 Monitor 的 WaitSet

自定义锁 - 不可重入锁

测试一

package com.rui.eight;

import lombok.extern.slf4j.Slf4j;

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

/**
 * 测试
 */
@Slf4j(topic = "c.Test13")
public class Test13 {
    public static void main(String[] args) {
        MyLock lock = new MyLock();
        new Thread(() -> {
            log.debug("t1 begin...");
            lock.lock();
            try {
                log.debug("t1 lock...");
            } finally {
                log.debug("t1 unlock...");
                lock.unlock();
            }
        }, "t1").start();

        new Thread(() -> {
            log.debug("t2 begin...");
            lock.lock();
            try {
                log.debug("t2 lock...");
            } finally {
                log.debug("t2 unlock...");
                lock.unlock();
            }
        }, "t2").start();
    }
}

/**
 * 自定义锁 - 不可重入锁
 */
class MyLock implements Lock {

    /**
     * 独占锁
     */
    class MySync extends AbstractQueuedSynchronizer {
        /**
         * 获取锁
         */
        @Override
        protected boolean tryAcquire(int arg) {
            if (compareAndSetState(0, 1)) {
                // 加锁成功,设置 owner 为当前线程
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        /**
         * 释放锁
         */
        @Override
        protected boolean tryRelease(int arg) {
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }

        /**
         * 是否拥有锁
         */
        @Override
        protected boolean isHeldExclusively() {
            return getState() == 1;
        }

        protected Condition newConditon() {
            return new ConditionObject();
        }
    }

    private MySync sync = new MySync();

    /**
     * 获取锁
     */
    @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.newConditon();
    }
}


// 某次运行结果

14:58:56 [t2] c.Test13 - t2 begin...
14:58:56 [t1] c.Test13 - t1 begin...
14:58:56 [t2] c.Test13 - t2 lock...
14:58:56 [t2] c.Test13 - t2 unlock...
14:58:56 [t1] c.Test13 - t1 lock...
14:58:56 [t1] c.Test13 - t1 unlock...

进程已结束,退出代码 0

测试二

package com.rui.eight;

import lombok.extern.slf4j.Slf4j;

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

/**
 * 测试
 */
@Slf4j(topic = "c.Test13")
public class Test13 {
    public static void main(String[] args) {
        MyLock lock = new MyLock();
        new Thread(() -> {
            log.debug("t1 begin...");
            lock.lock();
            log.debug("t1 lock...");
            lock.lock();
            log.debug("t1 relock...");
            try {
                log.debug("t1");
            } finally {
                log.debug("t1 unlock...");
                lock.unlock();
            }
        }, "t1").start();
    }
}

/**
 * 自定义锁 - 不可重入锁
 */
class MyLock implements Lock {

    /**
     * 独占锁
     */
    class MySync extends AbstractQueuedSynchronizer {
        /**
         * 获取锁
         */
        @Override
        protected boolean tryAcquire(int arg) {
            if (compareAndSetState(0, 1)) {
                // 加锁成功,设置 owner 为当前线程
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        /**
         * 释放锁
         */
        @Override
        protected boolean tryRelease(int arg) {
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }

        /**
         * 是否拥有锁
         */
        @Override
        protected boolean isHeldExclusively() {
            return getState() == 1;
        }

        protected Condition newConditon() {
            return new ConditionObject();
        }
    }

    private MySync sync = new MySync();

    /**
     * 获取锁
     */
    @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.newConditon();
    }
}


// 某次运行结果

15:02:01 [t1] c.Test13 - t1 begin...
15:02:01 [t1] c.Test13 - t1 lock...

8.3 ReentrantLock 原理

8.3.1 非公平锁(NonfairSync)实现原理 

加锁解锁流程 

1. Thread-0 获取锁

 

第一步,执行 compareAndSetState(0, 1) 

        将锁状态由 0 修改为 1,compareAndSetState(0, 1)  返回结果为 true,表明 Thread-0 获取锁成功

第二步,执行 setExclusiveOwnerThread(Thread.currentThread()) 

        设置 Owner 为 Thread-0

2. Thread-1 获取锁

 

第一步, 执行 compareAndSetState(0, 1) 

        因为此时 state 的值为 1,所以 compareAndSetState(0, 1)  返回结果为 false,表明 Thread-1 获取锁失败

第二步,执行 acquire(1)

第 1 步,执行  !tryAcquire(arg) 

        Thread-1 再次获取锁,因为 Thread-0 未释放锁,所以 !tryAcquire(arg) 返回结果为 true,表明 Thread-1 再次获取锁失败

第 2 步,执行 addWaiter(Node.EXCLUSIVE)

 

        addWaiter 方法的作用是构造 Node 队列并为当前线程创建节点并将其排入队列

        Node 队列,即等待队列(类似于 Monitor 的 EntryList),双端队列

        Node 队列的创建是懒惰的,hand 是队列的头部,tail 是队列的尾部

        Node 队列被创建时,addWaiter 方法会创建两个节点

        第一个 Node 节点称为 Dummy(哑元)或哨兵,用来占位,并不关联线程

        第二个 Node 节点关联当前线程 

        图中黄色三角表示该 Node 节点 waitStatus(状态)

        Node.EXCLUSIVE 表示独占,Node.SHARED 表示共享 

第 3 步,执行 acquireQueued(addWaiter(Node.EXCLUSIVE), arg))

第 ① 步,执行 p == head 

        p 为 node.predecessor(),即关联当前线程的 Node 节点的前驱节点

        因为关联 Thread-1 的 Node 节点的前驱节点为 Dummy,且 Dummy 是队列的头部,所以 p == head 的返回结果为 true

第 ② 步,执行 tryAcquire(arg)

        Thread-1 再次获取锁,因为 Thread-0 未释放锁,所以 tryAcquire(arg) 返回结果为 false,表明 Thread-1 获取锁失败 

第 ③ 步,执行 shouldParkAfterFailedAcquire(p, node) 

        检查 p 的 waitStatus,若为 -1,则返回 true;若为 0,则修改为 -1 并返回 false

        因为此时 Dummy 的 waitStatus 为 0,所以 shouldParkAfterFailedAcquire(p, node) 返回结果为 false

回到 for 循环初始,待再次执行到第 ③步

 

        因为此时 Dummy 的 waitStatus 为 -1,所以 shouldParkAfterFailedAcquire(p, node) 返回结果为 true

第 ④ 步,执行 parkAndCheckInterrupt()

        阻塞

private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this);
    return Thread.interrupted();
}

3. Thread-2、Thread-3 获取锁 

 

4. Thread-0 释放锁

 

第一步,执行 tryRelease(arg)

 

        设置 Owner 为 null,并将锁状态由 1 修改为 0

        tryRelease(arg) 返回结果为 true

        【不公平的体现】

第二步,执行 h != null

        h 为 head

        检查 Node 队列是否为空

        因为此时 head 为 Dummy,所以 h != null 返回结果为 true

第三步,执行 h.waitStatus != 0

        因为此时 Dummy 的 waitStatus 为 -1,所以 h.waitStatus != 0 返回结果为 true

第四步,执行 unparkSuccessor(h)

        唤醒 h 的后继结点(如果存在)

        Thread-1 被唤醒

第五步,执行 p == head 

        p 为 Dummy

        因为 Dummy 是队列的头部,所以 p == head 的返回结果为 true

第六步,执行 tryAcquire(arg)

        Thread-1 第五次获取锁,因为 Thread-0 已释放锁,所以 tryAcquire(arg) 返回结果为 true,表明 Thread-1 获取锁成功

        将锁状态由 0 修改为 1,并设置 Owner 为 Thread-1

        将关联 Thread-1 的 Node 节点设置为 Node 队列的头部,并将该节点关联的线程设置为 null

5. 不公平的体现

 

Thread-4 获取锁 

【接第一步】

第二步, 执行 compareAndSetState(0, 1) 

        将锁状态由 0 修改为 1,compareAndSetState(0, 1)  返回结果为 true,表明 Thread-4 获取锁成功

第二步,执行 setExclusiveOwnerThread(Thread.currentThread()) 

        设置 Owner 为 Thread-4

[Thread-1 流程略]

并发编程就更到这了,P241 

说些废话

本篇文章为博主日常学习记录,故而会概率性地存在各种错误,若您在浏览过程中发现一些,请在评论区指正,望我们共同进步,谢谢!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值