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

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

相关推荐
figo10tf10 分钟前
Spring Boot项目集成Redisson 原始依赖与 Spring Boot Starter 的流程
java·spring boot·后端
zhangyi_viva13 分钟前
Spring Boot(七):Swagger 接口文档
java·spring boot·后端
橙露18 分钟前
Spring Boot 核心原理:自动配置机制与自定义 Starter 开发
java·数据库·spring boot
小程故事多_8019 分钟前
Agent Infra核心技术解析:Sandbox sandbox技术原理、选型逻辑与主流方案全景
java·开发语言·人工智能·aigc
冰暮流星19 分钟前
sql语言之分组语句group by
java·数据库·sql
望舒51321 分钟前
代码随想录day25,回溯算法part4
java·数据结构·算法·leetcode
米羊12122 分钟前
已有安全措施确认(中)
网络
黎雁·泠崖23 分钟前
【魔法森林冒险】3/14 Allen类(一):主角核心属性与初始化
java·开发语言
黎雁·泠崖27 分钟前
【魔法森林冒险】1/14 项目总览:用Java打造你的第一个回合制冒险游戏
java·开发语言
NuageL33 分钟前
原始Json字符串转化为Java对象列表/把中文键名变成英文键名
java·spring boot·json