使用AQS实现自己的重入锁(独占、非公平)

版权声明: https://blog.csdn.net/Dongguabai/article/details/84188115

相关博客:

https://blog.csdn.net/Dongguabai/article/details/84178241

https://blog.csdn.net/Dongguabai/article/details/82691626

https://blog.csdn.net/Dongguabai/article/details/82461675

https://blog.csdn.net/Dongguabai/article/details/84142037

在JDK文档中有这么一段描述:

应该将子类定义为非公共内部帮助器类,可用它们来实现其封闭类的同步属性。类 AbstractQueuedSynchronizer 没有实现任何同步接口。而是定义了诸如 acquireInterruptibly(int) 之类的一些方法,在适当的时候可以通过具体的锁和相关同步器来调用它们,以实现其公共方法

 接下来照葫芦画瓢。

先不实现重入功能,只实现锁的功能:

package com.example.demoClient;

import java.io.Serializable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
 * @author Dongguabai
 * @date 2018/11/17 14:52
 */
public class MyLock implements Serializable, Lock {

    private final MySync sync;

    public MyLock() {
        this.sync = new MySync();
    }

    //照抄
    @Override
    public void lock() {
        sync.acquire(1);
    }

    //照抄
    @Override
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

    //类似照抄,这里不分公平非公平
    @Override
    public boolean tryLock() {
        return sync.tryAcquire(1);
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(time));
    }

    //照抄
    @Override
    public void unlock() {
        sync.release(1);
    }

    //照抄
    @Override
    public Condition newCondition() {
        return sync.newCondition();
    }

    static class MySync extends AbstractQueuedSynchronizer {
        @Override
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            //第一个线程进来,可以获得锁
            //第二个线程进来,拿不到锁

            //获取状态
            int state = getState();
            if (state == 0) {  //初始值,第一次进来
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            return false;
        }

        @Override
        protected final boolean tryRelease(int releases) {
            //首先要注意的是锁的获取和释放需要一一对应,也就是说调用此方法的线程一定是当前线程
            if (Thread.currentThread() != getExclusiveOwnerThread()) {
                throw new IllegalMonitorStateException();
            }
            //其实这里releases就是1
            int c = getState() - releases;
            boolean free = false;
            if (c == 0) {
                setExclusiveOwnerThread(null);
                free = true;
            }
            setState(c);
            return free;
        }

        //照抄
        final Condition newCondition() {
            return new ConditionObject();
        }
    }

}

再来测试之前提到过的线程不安全的例子(具体可参看:https://blog.csdn.net/Dongguabai/article/details/83931773):

package com.example.demoClient;

/**
 * @author Dongguabai
 * @date 2018/11/10 16:53
 */
public class Demo3 {

    MyLock myLock = new MyLock();

    private int value = 1;

    public static void main(String[] args) {
        Demo3 demo3 = new Demo3();
        new Thread(() -> {
            while (true) {
                System.out.println("用户" + Thread.currentThread().getName() + "买了第" + demo3.increamentAndGet() + "张票");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "张三").start();
        new Thread(() -> {
            while (true) {
                System.out.println("用户" + Thread.currentThread().getName() + "买了第" + demo3.increamentAndGet() + "张票");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "王五").start();
        new Thread(() -> {
            while (true) {
                System.out.println("用户" + Thread.currentThread().getName() + "买了第" + demo3.increamentAndGet() + "张票");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "李四").start();

    }

    //synchronized
    public int increamentAndGet() {
        myLock.lock();
        value++;
        myLock.unlock();
        return value;
    }
}

测试通过:

再来测试重入:

package com.example.demoClient;

/**
 * @author Dongguabai
 * @date 2018/11/16 15:58
 */
public class Test {

    private MyLock lock = new MyLock();

    public void a(){
        lock.lock();
        System.out.println("aaaaaaaaaaaaaaaaaaaaaa");
        b();
        lock.unlock();
    }

    public void b(){
        lock.lock();
        System.out.println("bbbbbbbbbbbbbbbbbbbbbb");
        lock.unlock();
    }

    public static void main(String[] args) {
        Test test = new Test();
        new Thread(()->{
            test.a();
        },"xxxxxxxxxxxxxxxxxxx").start();
    }
}

运行结果:

查看线程状态:

线程处于WAITING状态。简单分析为什么线程会处于WAITING状态:

原因主要跟state有关(具体可参看https://blog.csdn.net/Dongguabai/article/details/84178241或者https://blog.csdn.net/Dongguabai/article/details/82461675):

结合代码:

第一次进来state=0且将state设置为了1,后续直接就return false了。相当于代码的意思是第一个线程进来,可以获得到锁,但是第二个线程进来的时候要分为两种情况,第一种是如果当前进来的线程和当前独占的线程是同一个线程的话,是需要放行并且记录状态的,如果不是,直接return false。

修改后的锁:

package com.example.demoClient;

import java.io.Serializable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
 * @author Dongguabai
 * @date 2018/11/17 14:52
 */
public class MyLock implements Serializable, Lock {

    private final MySync sync;

    public MyLock() {
        this.sync = new MySync();
    }

    //照抄
    @Override
    public void lock() {
        sync.acquire(1);
    }

    //照抄
    @Override
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

    //类似照抄,这里不分公平非公平
    @Override
    public boolean tryLock() {
        return sync.tryAcquire(1);
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(time));
    }

    //照抄
    @Override
    public void unlock() {
        sync.release(1);
    }

    //照抄
    @Override
    public Condition newCondition() {
        return sync.newCondition();
    }

    static class MySync extends AbstractQueuedSynchronizer {
        @Override
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            //第一个线程进来,可以获得锁
            //第二个线程进来,拿不到锁,如果当前进来的线程和当前独占的线程是同一个线程的话,是需要放行并且记录状态的,如果不是,直接return false
            //获取状态
            int state = getState();
            if (state == 0) {  //初始值,第一次进来
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            } else if (current == getExclusiveOwnerThread()) {
                //这里没有线程安全问题,直接相加
                int nextc = state + acquires;
                if (nextc < 0) {// overflow
                    throw new Error("Maximum lock count exceeded");
                }
                //这里没有线程安全问题,直接设置
                setState(nextc);
                return true;
            }
            return false;
        }

        @Override
        protected final boolean tryRelease(int releases) {
            //首先要注意的是锁的获取和释放需要一一对应,也就是说调用此方法的线程一定是当前线程
            if (Thread.currentThread() != getExclusiveOwnerThread()) {
                throw new IllegalMonitorStateException();
            }
            //其实这里releases就是1
            int c = getState() - releases;
            boolean free = false;
            if (c == 0) {
                setExclusiveOwnerThread(null);
                free = true;
            }
            setState(c);
            return free;
        }

        //照抄
        final Condition newCondition() {
            return new ConditionObject();
        }
    }

}

再来测试重入问题:

正常运行。

猜你喜欢

转载自blog.csdn.net/Dongguabai/article/details/84188115
今日推荐