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

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

相关推荐
就叫_这个吧4 小时前
Java注解、元注解、自定义注解定义及应用
java·开发语言·注解
Sam_Deep_Thinking4 小时前
聊聊Java中的of
java·开发语言·架构
网安小白的进阶之路5 小时前
B模块 安全通信网络 第二门课IPv6与WLAN 01
网络·安全
学习3人组5 小时前
Cisco ASA防火墙 NAT实验:源NAT+目的NAT(Trust/Untrust双区域,无DMZ)
网络·网络安全
NE_STOP5 小时前
Docker--管理监控平台的应用
java
爱吃羊的老虎6 小时前
【JAVA】python转java:Spring Boot 入门
java·spring boot·python
天天进步20156 小时前
Tunnelto 源码解析 #6:数据转发核心:远端 HTTP 请求如何被转发到本地 localhost
网络
Love_云宝儿6 小时前
GeoJSON简介
java·gis·地图·jts
摇滚侠7 小时前
JDBC 基础到高级一套通关!进阶篇 16-27
java
志栋智能7 小时前
安全超自动化:应对海量安全警报的唯一解
网络·安全·自动化