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]);
    }
}

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

相关推荐
草履虫建模6 小时前
力扣算法 1768. 交替合并字符串
java·开发语言·算法·leetcode·职场和发展·idea·基础
wenzhangli78 小时前
OoderAgent SDK(0.6.6) UDP通讯与协议测试深度解析
网络·网络协议·udp
安科士andxe8 小时前
60km 远距离通信新选择:AndXe SFP-155M 单模单纤光模块深度测评
网络·信息与通信
qq_297574678 小时前
【实战教程】SpringBoot 实现多文件批量下载并打包为 ZIP 压缩包
java·spring boot·后端
老毛肚8 小时前
MyBatis插件原理及Spring集成
java·spring·mybatis
学嵌入式的小杨同学8 小时前
【Linux 封神之路】信号编程全解析:从信号基础到 MP3 播放器实战(含核心 API 与避坑指南)
java·linux·c语言·开发语言·vscode·vim·ux
lang201509288 小时前
JSR-340 :高性能Web开发新标准
java·前端·servlet
Re.不晚9 小时前
Java入门17——异常
java·开发语言
酥暮沐9 小时前
iscsi部署网络存储
linux·网络·存储·iscsi
缘空如是9 小时前
基础工具包之JSON 工厂类
java·json·json切换