AQS简单源码思路和手撕实现

AQS

不可重入锁的实现

java 复制代码
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;

public class MyLock {
    AtomicBoolean flag = new AtomicBoolean(false);
    Thread owner = null;


    AtomicReference<Node> head = new AtomicReference<>(new Node());
    AtomicReference<Node> tail = new AtomicReference<>(head.get());

    //锁的原理是拿不到就在一直阻塞在函数里,拿到了就返回
    void lock() {
        //下面这段代码存在会导致成为非公平锁,注释掉就变成了公平锁
        if (flag.compareAndSet(false, true)) {
            System.out.println(Thread.currentThread().getName()+"直接拿到锁");
            owner = Thread.currentThread();
            return;
        }

        Node current = new Node();
        current.thread = Thread.currentThread();

        while (true) {
            //尾插
            Node currentTail = tail.get();
            if (tail.compareAndSet(currentTail, current)) {
                System.out.println(Thread.currentThread().getName()+"加入到了链表");
                current.pre = currentTail;
                currentTail.next = current;
                break;
            }
        }
        while (true) {
            //每一次只唤醒head节点的下一个节点
            if (current.pre == head.get() && flag.compareAndSet(false, true)) {
                owner = Thread.currentThread();
                //只有持有锁的线程才能到这步,所以不需要cas
                head.set(current);
                //断掉这个节点和head结点的关系
                current.pre.next = null;
                current.pre = null;
                System.out.println(Thread.currentThread().getName()+"被唤醒之后拿到锁");
                return;
            }
            LockSupport.park();
        }
    }

    void unlock() {
        if (Thread.currentThread() != owner) {
            throw new IllegalStateException("当前线程没锁无法释放锁");
        }
        //只唤醒head节点的下一个节点就可以
        Node headNode = head.get();
        Node next = headNode.next;
        //只有持有锁的线程才能到这步,所以不需要cas
        flag.set(false);
        if (next != null) {
            System.out.println(Thread.currentThread().getName()+"唤醒了"+next.thread.getName());
            LockSupport.unpark(next.thread);
        }

    }

    class Node {
        Node pre;
        Node next;
        Thread thread;

        Node() {
        }
    }
}

可重入锁的实现

java 复制代码
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;

public class MyLock {
    AtomicInteger flag = new AtomicInteger(0);
    Thread owner = null;


    AtomicReference<Node> head = new AtomicReference<>(new Node());
    AtomicReference<Node> tail = new AtomicReference<>(head.get());

    //锁的原理是拿不到就在一直阻塞在函数里,拿到了就返回
    void lock() {
        if (flag.get() == 0) {
            //下面这段代码存在会导致成为非公平锁,注释掉就变成了公平锁
            if (flag.compareAndSet(0, 1)) {
                System.out.println(Thread.currentThread().getName() + "直接拿到锁");
                owner = Thread.currentThread();
                return;
            }
        } else {
            if (owner == Thread.currentThread()) {
                //get和increase就像++i 和 i++的关系
                System.out.println(Thread.currentThread().getName()+"拿到了重入锁,当前重入次数为"+flag.incrementAndGet());
                return;
            }
        }

        Node current = new Node();
        current.thread = Thread.currentThread();

        while (true) {
            //尾插
            Node currentTail = tail.get();
            if (tail.compareAndSet(currentTail, current)) {
                System.out.println(Thread.currentThread().getName() + "加入到了链表");
                current.pre = currentTail;
                currentTail.next = current;
                break;
            }
        }
        while (true) {
            //每一次只唤醒head节点的下一个节点
            if (current.pre == head.get() && flag.compareAndSet(0, 1)) {
                owner = Thread.currentThread();
                //只有持有锁的线程才能到这步,所以不需要cas
                head.set(current);
                //断掉这个节点和head结点的关系
                current.pre.next = null;
                current.pre = null;
                System.out.println(Thread.currentThread().getName() + "被唤醒之后拿到锁");
                return;
            }
            LockSupport.park();
        }
    }

    void unlock() {
        if (Thread.currentThread() != owner) {
            throw new IllegalStateException("当前线程没锁无法释放锁");
        }
        int i = flag.get();
        if (i > 1) {
            flag.set(i - 1);
            System.out.println(Thread.currentThread().getName()+"解锁了重入锁,重入次数为:"+(i-1));
            return;
        }
        if(i<=0){
            throw new IllegalStateException("重入锁解锁错误");
        }
        //只唤醒head节点的下一个节点就可以
        Node headNode = head.get();
        Node next = headNode.next;
        //只有持有锁的线程才能到这步,所以不需要cas
        flag.set(0);
        if (next != null) {
            System.out.println(Thread.currentThread().getName() + "唤醒了" + next.thread.getName());
            LockSupport.unpark(next.thread);
        }

    }

    class Node {
        Node pre;
        Node next;
        Thread thread;

        Node() {
        }
    }
}

过程:

java 复制代码
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport;

public class MyLock {
    AtomicBoolean flag = new AtomicBoolean(false);
    Thread owner = null;

    //锁的原理是拿不到就在一直阻塞在函数里,拿到了就返回
    void lock() {
        while (true) {
            if (flag.compareAndSet(false, true)) {
                owner = Thread.currentThread();
                return;
            }
            LockSupport.park();
        }
    }

    void unlock() {
        if (Thread.currentThread() != owner) {
            throw new IllegalStateException("当前线程没锁无法释放锁");
        }
        while (true) {
            if (flag.compareAndSet(true, false)) {
                return;
            }
        }
    }
}

现在是一个简单的雏形,但是涉及到休眠和唤醒不知道找到对应线程所以引入链表。

这里还要防止多线程的问题,所以使用了AtomicBoolean针对基本数据类型的原子类和AtomicReference针对引用数据类型的原子类

(多线程涉及的地方很多都要把if改成while来不断尝试)

这里的代码的写法很优雅,我照着视频敲得时候如是说道。

在苏醒的时候要防止虚假苏醒要加上条件才能返回。

再完善下就变成了:

java 复制代码
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;

public class MyLock {
    AtomicBoolean flag = new AtomicBoolean(false);
    Thread owner = null;


    AtomicReference<Node> head = new AtomicReference<>(new Node());
    AtomicReference<Node> tail = new AtomicReference<>(head.get());

    //锁的原理是拿不到就在一直阻塞在函数里,拿到了就返回
    void lock() {
        //下面这段代码存在会导致成为非公平锁,注释掉就变成了公平锁
        if (flag.compareAndSet(false, true)) {
            System.out.println(Thread.currentThread().getName()+"直接拿到锁");
            owner = Thread.currentThread();
            return;
        }

        Node current = new Node();
        current.thread = Thread.currentThread();

        while (true) {
            //尾插
            Node currentTail = tail.get();
            if (tail.compareAndSet(currentTail, current)) {
                System.out.println(Thread.currentThread().getName()+"加入到了链表");
                current.pre = currentTail;
                currentTail.next = current;
                break;
            }
        }
        while (true) {
            //每一次只唤醒head节点的下一个节点
            if (current.pre == head.get() && flag.compareAndSet(false, true)) {
                owner = Thread.currentThread();
                //只有持有锁的线程才能到这步,所以不需要cas
                head.set(current);
                //断掉这个节点和head结点的关系
                current.pre.next = null;
                current.pre = null;
                System.out.println(Thread.currentThread().getName()+"被唤醒之后拿到锁");
                return;
            }
            LockSupport.park();
        }
    }

    void unlock() {
        if (Thread.currentThread() != owner) {
            throw new IllegalStateException("当前线程没锁无法释放锁");
        }
        //只唤醒head节点的下一个节点就可以
        Node headNode = head.get();
        Node next = headNode.next;
        //只有持有锁的线程才能到这步,所以不需要cas
        flag.set(false);
        if (next != null) {
            System.out.println(Thread.currentThread().getName()+"唤醒了"+next.thread.getName());
            LockSupport.unpark(next.thread);
        }

    }

    class Node {
        Node pre;
        Node next;
        Thread thread;

        Node() {
        }
    }
}

基本的非公平不可重入锁AQS(注释一段代码就变成了公平锁)

java 复制代码
import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        int count[] = new int[]{1000};
        List<Thread> threads = new ArrayList<>();
        MyLock myLock = new MyLock();
        for (int i = 0; i < 100; i++) {
            threads.add(new Thread(() -> {
                myLock.lock();
                for (int i1 = 0; i1 < 10; i1++) {
                    count[0]--;
                }
                myLock.unlock();
            }));
        }
        for (Thread thread : threads) {
            thread.start();
        }
        for (Thread thread : threads) {
            thread.join();
        }
        System.out.println(count[0]);
    }
}

对应测试成功变为0

接下来分析考虑可重入:

复制代码
flag.set(false);

注意这个的位置,这句过后就说明别的线程也能过来竞争了,后面拿到的head就可能不安全

java 复制代码
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;

public class MyLock {
    AtomicInteger flag = new AtomicInteger(0);
    Thread owner = null;


    AtomicReference<Node> head = new AtomicReference<>(new Node());
    AtomicReference<Node> tail = new AtomicReference<>(head.get());

    //锁的原理是拿不到就在一直阻塞在函数里,拿到了就返回
    void lock() {
        if (flag.get() == 0) {
            //下面这段代码存在会导致成为非公平锁,注释掉就变成了公平锁
            if (flag.compareAndSet(0, 1)) {
                System.out.println(Thread.currentThread().getName() + "直接拿到锁");
                owner = Thread.currentThread();
                return;
            }
        } else {
            if (owner == Thread.currentThread()) {
                //get和increase就像++i 和 i++的关系
                System.out.println(Thread.currentThread().getName()+"拿到了重入锁,当前重入次数为"+flag.incrementAndGet());
                return;
            }
        }

        Node current = new Node();
        current.thread = Thread.currentThread();

        while (true) {
            //尾插
            Node currentTail = tail.get();
            if (tail.compareAndSet(currentTail, current)) {
                System.out.println(Thread.currentThread().getName() + "加入到了链表");
                current.pre = currentTail;
                currentTail.next = current;
                break;
            }
        }
        while (true) {
            //每一次只唤醒head节点的下一个节点
            if (current.pre == head.get() && flag.compareAndSet(0, 1)) {
                owner = Thread.currentThread();
                //只有持有锁的线程才能到这步,所以不需要cas
                head.set(current);
                //断掉这个节点和head结点的关系
                current.pre.next = null;
                current.pre = null;
                System.out.println(Thread.currentThread().getName() + "被唤醒之后拿到锁");
                return;
            }
            LockSupport.park();
        }
    }

    void unlock() {
        if (Thread.currentThread() != owner) {
            throw new IllegalStateException("当前线程没锁无法释放锁");
        }
        int i = flag.get();
        if (i > 1) {
            flag.set(i - 1);
            System.out.println(Thread.currentThread().getName()+"解锁了重入锁,重入次数为:"+(i-1));
            return;
        }
        if(i<=0){
            throw new IllegalStateException("重入锁解锁错误");
        }
        //只唤醒head节点的下一个节点就可以
        Node headNode = head.get();
        Node next = headNode.next;
        //只有持有锁的线程才能到这步,所以不需要cas
        flag.set(0);
        if (next != null) {
            System.out.println(Thread.currentThread().getName() + "唤醒了" + next.thread.getName());
            LockSupport.unpark(next.thread);
        }

    }

    class Node {
        Node pre;
        Node next;
        Thread thread;

        Node() {
        }
    }
}

主要是对加锁和解锁周围的和本来flag在true和false转化变为要对1,0的一些判断

java 复制代码
import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        int count[] = new int[]{1000};
        List<Thread> threads = new ArrayList<>();
        MyLock myLock = new MyLock();
        for (int i = 0; i < 5; i++) {
            threads.add(new Thread(() -> {
                for (int i1 = 0; i1 < 5; i1++) {
                    myLock.lock();
                    count[0]--;
                }
                //可重入锁要保证释放锁的次数和上锁的次数相同
                for (int i1 = 0; i1 < 5; i1++) {
                    myLock.unlock();
                }

            }));
        }
        for (Thread thread : threads) {
            thread.start();
        }
        for (Thread thread : threads) {
            thread.join();
        }
        System.out.println(count[0]);
    }
}

整体来说,实现起来比较烧脑,需要有清晰的逻辑,可以多绕几遍

相关推荐
Aision_44 分钟前
从工具调用到 MCP、Skill完整学习记录
java·python·gpt·学习·langchain·prompt·agi
zc.z5 小时前
JAVA实现:纯PCM格式音频转换成BASE64
java·音视频·pcm
mask哥5 小时前
力扣算法java实现汇总整理(上)
java·算法·leetcode
Aaswk6 小时前
Java Lambda 表达式与流处理
java·开发语言·python
是宇写的啊7 小时前
Spring AOP
java·spring
苍煜7 小时前
Docker容器网络详解+端口映射原理(系列第二篇:实战核心)
网络·docker·容器
万邦科技Lafite7 小时前
京东item_get接口实战案例:实时商品价格监控全流程解析
java·开发语言·数据库·python·开放api·淘宝开放平台
初願致夕霞8 小时前
基于系统调用的Linux网络编程——UDP与TCP
linux·网络·c++·tcp/ip·udp
Mr_pyx8 小时前
Spring AI 入门教程:Java开发者的AI应用捷径
java·人工智能·spring
Zephyr_08 小时前
Leedcode算法题
java·算法