基于 ReentrantLock实现一个锁

package aqsLock;

import java.util.concurrent.locks.AbstractQueuedSynchronizer;


/**
 * @Author: Kayleh
 * @Date: 2021/4/20 0:57
 */
public class aqsLock
{
    public void lock()
    {
        sync.acquire(1);
    }

    public void unlock()
    {
        sync.release(1);
    }

    private final Sync sync = new Sync();

    public static class Sync extends AbstractQueuedSynchronizer
    {
        @Override
        protected boolean tryAcquire(int arg)
        {
            // CAS 方式尝试获取锁,成功返回true,失败返回false
            if (compareAndSetState(0, 1))
                return true;
            return false;
        }

        @Override
        protected boolean tryRelease(int arg)
        {
            // 释放锁
            setState(0);
            return true;
        }
    }
}

基本功能实现,测试:

// 可能发生线程安全问题的共享变量
private static long count = 0;

// 两个线程并发对 count++
public static void main(String[] args) throws Exception {
    // 创建两个线程,执行add()操作
    Thread th1 = new Thread(()-> add());
    Thread th2 = new Thread(()-> add());
    // 启动两个线程
    th1.start();
    th2.start();
    // 等待两个线程执行结束
    th1.join();
    th2.join();
    // 这里应该是 20000 就对了,说明锁生效了
    System.out.println(count);
}

// 我画了一上午写出来的锁,哈哈
private static ExampleLock exampleLock = new ExampleLock();

// 循环 count++,进行 10000 次
private static void add() {
    exampleLock.lock();
    for (int i = 0; i < 10000; i++) {
        count++;
    }
    add2();
    // 没啥异常,我就直接释放锁了
    exampleLock.unlock();
}

非公平锁,因为线程抢锁不排队,纯看脸。

实现的排队获取锁,叫公平锁,因为只要有线程在排队,新来的就得乖乖去排队,不能直接抢。

实现一个公平锁

@Override
public boolean tryAcquire(int acquires) {
    // 原有基础上加上这个
    if (有线程在等待队列中) {
        // 返回获取锁失败,AQS会帮我把该线程放在等待队列队尾的
        return false;
    }
    if (compareAndSetState(0, 1)) {
        return true;
    }
    return false;
}

代码:

package aqsLock;

import java.util.concurrent.locks.AbstractQueuedSynchronizer;


/**
 * @Author: Kayleh
 * @Date: 2021/4/20 0:57
 */
public class aqsLock
{
    public aqsLock(boolean fair)
    {
        sync = fair ? new Sync() : new NofairSync();
    }


    public void lock()
    {
        sync.acquire(1);
    }

    public void unlock()
    {
        sync.release(1);
    }

    private Sync sync = new Sync();

    public static class Sync extends AbstractQueuedSynchronizer
    {
        @Override
        protected boolean tryAcquire(int arg)
        {
            // CAS 方式尝试获取锁,成功返回true,失败返回false
            if (hasQueuedPredecessors() && compareAndSetState(0, 1))
                return true;
            return false;
        }

        @Override
        protected boolean tryRelease(int arg)
        {
            // 释放锁
            setState(0);
            return true;
        }
    }

    public static class NofairSync extends Sync
    {
        @Override
        protected boolean tryAcquire(int arg)
        {
            // CAS 方式尝试获取锁,成功返回true,失败返回false
            if (compareAndSetState(0, 1))
                return true;
            return false;
        }

        @Override
        protected boolean tryRelease(int arg)
        {
            // 释放锁
            setState(0);
            return true;
        }
    }
}

工具会导致一个线程卡死,一直获取不到锁

实现方法可以重入

public void doSomeThing2() {
    flashLock.lock();
    doSomeThing2();
    flashLock.unlock();
}

public void doSomeThing2() {
    flashLock.lock();
    ...
    flashLock.unlock();
}

一个线程执行了一个方法,获取了锁,这个方法没有结束,又调用了另一个需要锁的方法,于是卡在这再也不走了。

怎么 让同一个线程持有锁时,还能继续获取锁(可重入),只有当不同线程才互斥呢?

@Override
public boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        if (hasQueuedPredecessors() && compareAndSetState(0, 1)) {
            // 拿到锁记得记录下持锁线程是自己
            setExclusiveOwnerThread(current);
            return true;
        }
    } else if (current == getExclusiveOwnerThread()) {
        // 看见锁被占了(state!=0)也别放弃,看看是不是自己占的
        setState(c + acquires);
        return true;
    }
    return false;
}

评论