AQS思想源码解析

基本概念

只有操作系统的内核才能提供创建线程的能力,java的线程也就是调用了内核提供的api接口 clone.
java调用内核的方式称为 系统调用

线程的创建和使用

创建线程的方式一:继承Thread类,重写run 方法,调用start方法。

public class MyThread extends Thread {

    @Override
    public void run() {
        System.out.println("自定义方法"+Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
    }
}

start 方法只能调用一次!

public class MyThread2 extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+"----"+i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyThread2 myThread2 = new MyThread2();
        myThread2.setName("子线程");
        myThread2.start();
        
        for (int i = 0; i < 100; i++) {
            System.out.println("主线程"+i);
            if (i == 44){
<!--            暂停当前线程,等待mythread2 线程执行结束再执行主线程  -->
                myThread2.join();
            }
        }

    }
}
  1. stop() 强制结束当前线程,已经过时了
  2. sleep() cpu在指定时间内不会执行,该线程也不会释放资源
  3. isAlive() 判断当前线程是否还存在
    创建线程的方式二:实现runnable 接口,放入Thread的构造方法中
class MyRunnable implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+i);
        }
    }
}
public class MyThread {
    public static void main(String[] args) {

        new Thread(new MyRunnable()).start();

        for (int i = 0; i < 100; i++) {
            System.out.println("主线程"+i);
        }
    }
}

创建线程的方式三: 实现callable 接口,放入 FutureTask中,然后再放入Thread类中

class MyCallable implements Callable<String>{
    @Override
    public String call() throws Exception {
        for (int i = 0; i < 10; i++) {
            Thread.sleep(1000);
            System.out.println("执行了一次 "+Thread.currentThread().getName());
        }
        return "ok";
    }
}
public class FutureTaskTest {
    
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<String> task = new FutureTask<String>(new MyCallable());
        Thread thread = new Thread(task);
        thread.start();
        String s = task.get();
        System.out.println(s);
    }
}

线程的状态

  1. 新建状态(new) 当新建了一个 Thread 类之后,该线程就处于新建状态
  2. 就绪状态(runnable) 调用了Thread的start方法之后,线程变为就绪状态,等待cpu执行
  3. 阻塞状态(Blocking) 线程等待进入synchronized 关键字方法或者代码块时的状态
  4. 等待(waiting) 调用了wait() 或者 join() 方法之后,当前线程就处于等待状态,等待其他线程唤醒
  5. 计时等待(time waiting) 调用了 wait(time) join(time) Thread.sleep 方法之后,不会永远等待下去,时间到了会自动切换为runnable状态
  6. 终止(Terminated) 执行结束只有就变成终止状态

在这里插入图片描述

Synchronized

synchronized 被字节码编译后通过 monitorenter 和 monitorexit 指令来标记锁定的代码块,线程通过访问 锁定对象的markword 的两位来判断锁定信息
锁升级:
markword 记录这个线程的ID (偏向锁) 当这个线程再次进入的时候直接判断,就重入了
如果有线程争抢: 升级为自旋锁
10次以后,升级为重量级锁。 线程就变成Block状态 。 如果是 ReentrantLock 则是 被 LockSupport.park 变成 waiting状态

volatile

  1. 保证线程的可见性
    线程空间操作数据的时候会将主线程的数据拷贝到线程空间,然后最后在刷回主线程,被标记了volatile的对象,线程在操作的时候就是直接操作主线程的数据。
  2. 禁止指令重排

LockSupport

阻塞和唤醒线程的类,可以指定唤醒某个线程

  1. LockSupport.park(); 阻塞当前线程
  2. LockSupport.unpark(thread); 指定解锁某个线程
  3. thread.interrupt(); 中断后也会解锁线程 isInterrupted = true
        Thread thread = new Thread(() -> {
            System.out.println("睡眠了");
            LockSupport.park();
            System.out.println("醒了");
        });

        thread.start();
        thread.interrupt();
        LockSupport.unpark(thread);

AQS 相关

JUC并发包里面最重要的内容就是 AbstractQueuedSynchronizer 类,其他相关类都是基于 AQS得来的,我们先来看看其中是怎么实现的。
以及推导出其他类。
概念
AQS使用一个volatile的int类型成员变量来表示同步状态, 通过内置的 FIFO 队列来完成资源获取的排队工作,将每条要去抢占资源的线程 封装成一个 Node节点来实现锁的分配, 通过CAS完成state的修改。
AQS本身有一个state,表示锁的状态。Node里面也有一个 waitStatus 表示线程的状态。

AQS 的本质就是控制多个线程并发访问一个资源的时候,对于那些没有抢到资源的线程是如何维护的一个思想。

AQS分为独占和共享两种不同方式,公平和非公平很简单,公平就是队列里有线程的时候,新来的线程直接往队列里插。非公平就是就算队列里有线程,新来的还是会尝试去抢一下。重点是看独占和非独占。

AQS独占非独占思想

AQS中有一个资源的概念,就是成员变量state, 独占就是只能有一个线程操作资源,而非独占就是可以同时有N个线程操作资源(这部分逻辑是在子类的实现中完成的,例如Semaphore的判断逻辑是资源如果有剩余就返回成功,而RenentryLock则是如果资源是1了就返回失败)。 AQS抽象了几组方法来让子类自定义对资源操作成功的判断,如果获取资源失败的管理线程由AQS封装逻辑。
并且独占会将 AQS的 exclusiveOwnerThread 设为当前线程

子类实现的
  1. tryAcquire 独占方式占有资源
  2. tryRelease 独占方式释放资源
  3. tryAcquireShared 共享方式占有资源
  4. tryReleaseShared 共享方式释放资源

对应的,在AQS自身也有对应的方法来处理独占和非独占的逻辑

AQS中对应的逻辑
  1. acquire 独占方式占有资源, 当子类的 tryAcquire 获取失败的时候,就调用 addWaiter 创建节点,然后 acquireQueued 入队列
 public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
  1. release 独占方式释放资源, 当子类 tryRelease释放资源成功的时候,找到从头开始 unparkSuccessor 唤醒下一个节点 (还有判断逻辑)
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
  1. acquireShared 共享方式占有资源 . 当子类 tryAcquireShared 失败了(小于0表示失败), 就调用 doAcquireSharedInterruptibly 创建节点加入队列
    public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }
  1. releaseShared 共享方式释放资源, 当子类尝试释放资源成功的时候,调用 doReleaseShared
    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }

源码阅读

独占方式
  1. tryAcquire: 尝试获取锁,tryRelease 尝试释放锁 由子类实现
  2. addWaiter 将当前线程包装成一个 Node并插入队列中, 如果队列中没有数据,则头部先创建一个哨兵节点
    private Node addWaiter(Node mode) {
    	# 创建Node
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        # 插入队列尾部
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        # 要么CAS插入队列尾部失败,要么没有尾部指针
        enq(node);
        return node;
    }
  1. enq: 初始化队列或者自旋插入尾部 注意,这个方法是 unparkSuccessor 尾部唤醒的逻辑点
    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)) {
                // 假设线程1执行到这里被挂起,此时 next 指针还没有关联到,后新来的线程 n 可能已经被排列到后面去了,
               // 所以当 t 被需要时,它的 next 指针还没有设置或者重置;故需要从后到前寻找,而如果找寻下一个,而这个可能被遗漏了;
                    t.next = node;
                    return t;
                }
            }
        }
    }
  1. acquireQueued 在队列中的Node尝试获取锁,获取不到则陷入 SLEEP 状态
    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            # 自旋,只有获取到锁才能出来 
            # tryAcquire 尝试获取锁
            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);
        }
    }
  1. shouldParkAfterFailedAcquire 判断是不是要进入 Sleep阻塞是通过前一个节点的状态来判断
    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
            return true;
        if (ws > 0) {
        	# 如果是取消状态就直接把这个节点给删了
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }
  1. parkAndCheckInterrupt 睡眠
    private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }
  1. release : 释放资源,如果释放成功,则唤醒其他node
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
  1. unparkSuccessor 唤醒一个Node,继续循环 acquireQueued 方法。如果这个Node已经取消了,那么从 尾部找一个可用的唤醒,为什么是从尾部找?看 enq 方法解释
    private void unparkSuccessor(Node node) {
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread);
    }
  1. cancelAcquire 将中断的线程踢出队列。 测试过程中只有 doAcquireInterruptibly才能执行 acquireQueued执行不到

非独占

  1. acquireShared 子类尝试获取资源,如果失败 <0 的话,准备入队列
    public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }
  1. doAcquireShared 先创建节点 addWaiter, 然后再次尝试获取资源 tryAcquireShared 如果失败再判断状态 shouldParkAfterFailedAcquire,然后睡眠 parkAndCheckInterrupt . 非独占这里很多都和独占公用方法。
    private void doAcquireShared(int arg) {
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        if (interrupted)
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
  1. releaseShared 释放资源,先让子类尝试释放资源,如果成功则唤醒其他等待线程 doReleaseShared
    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }
  1. doReleaseShared 找到前置节点为 SIGNAL 也就是自己状态正确的节点唤醒
    private void doReleaseShared() {
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }

AQS子类实现

CountDownLatch

CountDownLatch 属于共享锁 ,先创建 N个资源,然后自己阻塞的等待什么时候资源变成0.

    public static void main(String[] args) throws Exception {
        CountDownLatch latch = new CountDownLatch(2);
        for (int i = 0; i < 2; i++) {
            new Thread(()->{
                System.out.println("do something");
                latch.countDown();
            }).start();
        }
        latch.await();
        System.out.println("finished");
    }
    -------------------CountDownLatch ------------------------
    public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }
    public void countDown() {
        sync.releaseShared(1);
    }

await 就是尝试获取资源的自定义实现,如何算获取到资源由 CountDownLatch 自己说了算 tryAcquireShared 方法。
countDown 就是尝试释放资源,如何算获取到资源由 CountDownLatch 自己说了算 tryReleaseShared 方法。

        protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
        }

        protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }

CountDownLatch 对于 获取到资源的定义就是 资源什么时候等于0就算获取成功。
对于 释放资源 的逻辑是CAS对状态 -1 操作,直到资源变为0了才会通知AQS唤醒等待的线程
在这里插入图片描述

Semaphore

Semaphore属于共享锁思想是 一开始创建N个资源, 多个线程尝试去消费资源 acquire, 如果资源不够就阻塞。消费结束之后释放资源 release

	    public static void main(String[] args) throws Exception {
	        Semaphore semaphore = new Semaphore(10);
	        semaphore.acquire(4);
	        semaphore.release(2);
	    }
	    # acquire 最终调用这个
        final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }
		# release 最终调用这个
        protected final boolean tryReleaseShared(int releases) {
            for (;;) {
                int current = getState();
                int next = current + releases;
                if (next < current) // overflow
                    throw new Error("Maximum permit count exceeded");
                if (compareAndSetState(current, next))
                    return true;
            }
        }

在这里插入图片描述

ReentrantLock

独占锁, 思想是每个线程都 尝试将资源设置为1(如果是自身线程,则往上叠加,可重入的概念), 释放资源的时候 就是将资源设置为0的时候就唤醒其他线程。

	    public static void main(String[] args) throws Exception {
	        ReentrantLock lock = new ReentrantLock();
	        lock.lock();
	        lock.unlock();
	    }
-------------------------------------------------------------------------------
        # lock 的逻辑最终调用
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
 		# unlock的逻辑最终调用
        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值