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

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

相关推荐
莫回首�3 小时前
ubuntu 20.04 多网卡配置,遇到问题总结
linux·网络·ubuntu
一定要AK5 小时前
Spring 入门核心笔记
java·笔记·spring
A__tao5 小时前
Elasticsearch Mapping 一键生成 Java 实体类(支持嵌套 + 自动过滤注释)
java·python·elasticsearch
KevinCyao6 小时前
java视频短信接口怎么调用?SpringBoot集成视频短信及回调处理Demo
java·spring boot·音视频
迷藏4946 小时前
**发散创新:基于Rust实现的开源合规权限管理框架设计与实践**在现代软件架构中,**权限控制(RBAC)** 已成为保障
java·开发语言·python·rust·开源
wuxinyan1237 小时前
Java面试题47:一文深入了解Nginx
java·nginx·面试题
新知图书7 小时前
搭建Spring Boot开发环境
java·spring boot·后端
冰河团队7 小时前
一个拉胯的分库分表方案有多绝望?整个部门都在救火!
java·高并发·分布式数据库·分库分表·高性能
星辰徐哥7 小时前
5G的行业应用:工业互联网、车联网、智慧医疗中的网络支撑
网络·5g·php