Java并发编程 -- AQS实战手写锁&可重入锁

阅读 40
收藏 1
2018-06-14
原文链接:www.jianshu.com

Java并发编程 -- AQS可能会看的一脸懵逼,今天实战一个项目练手AQS

MyAQSLock.java

/**
 * Created by Fant.J.
 */
public class MyAQSLock implements Lock {

    private Helper helper = new Helper();

    private class Helper extends AbstractQueuedSynchronizer {

        @Override
        protected boolean tryAcquire(int arg) {

            // 如果第一个线程进来,可以拿到锁,因此我们可以返回true

            // 如果第二个线程进来,则拿不到锁,返回false。有种特例,如果当前进来的线程和当前保存的线程是同一个线程,则可以拿到锁,但是有代价,要更新状态值

            // 如何判断是第一个线程进来还是其他线程进来?
            //获取状态值
            int state = getState();
            Thread t = Thread.currentThread();
            //如果状态=0,那就是第一个线程
            if (state == 0) {
                if (compareAndSetState(0, arg)) {
                    //设置当前线程为独占锁线程
                    setExclusiveOwnerThread(t);
                    return true;
                }
            } else if (getExclusiveOwnerThread() == t) {
                setState(state + 1);
                return true;
            }
            return false;
        }

        @Override
        protected boolean tryRelease(int arg) {

            // 锁的获取和释放肯定是一一对应的,那么调用此方法的线程一定是当前线程
            //获取当前线程,如果不等于独占锁的线程
            if (Thread.currentThread() != getExclusiveOwnerThread()) {
                throw new RuntimeException();
            }

            int state = getState() - arg;

            boolean flag = false;

            if (state == 0) {
                setExclusiveOwnerThread(null);
                flag = true;
            }

            setState(state);

            return flag;
        }

        Condition newCondition() {
            return new ConditionObject();
        }

    }

    @Override
    public void lock() {
        helper.acquire(1);
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
        helper.acquireInterruptibly(1);
    }

    @Override
    public boolean tryLock() {
        return helper.tryAcquire(1);
    }

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

    @Override
    public void unlock() {
        helper.release(1);
    }

    @Override
    public Condition newCondition() {
        return helper.newCondition();
    }
}

从上往下分析,首先继承Lock接口,然后定义一个子类为非公共内部帮助器类Helper类,Helper类继承AQS,重写它的tryAcquire和tryRelease方法。作为锁的获取和释放。然后填充Lock的子类实现。(为什么Lock子类方法里传值都是1呢,因为AQS源码就是这样,1一路传到底),注释还算详细,就不在这多说了。

/**
 * Created by Fant.J.
 */
class TestAQS {

    private int value;

    private MyAQSLock myAQSLock = new MyAQSLock();

    public int next(){
        myAQSLock.lock();
        try {
            Thread.sleep(300);
            return value++;
        } catch (InterruptedException e) {
            throw new RuntimeException();
        }finally {
            myAQSLock.unlock();
        }
    }
    public void a() {
        myAQSLock.lock();
        System.out.println("a");
        b();
        myAQSLock.unlock();
    }

    public void b() {
        myAQSLock.lock();
        System.out.println("b");
        myAQSLock.unlock();
    }
    public static void main(String[] args) {
        TestAQS test = new TestAQS();


        new Thread(new Runnable() {
            @Override
            public void run() {
                test.a();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    System.out.println(Thread.currentThread().getName()+"  "+test.next());
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    System.out.println(Thread.currentThread().getName()+"  "+test.next());
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    System.out.println(Thread.currentThread().getName()+"  "+test.next());
                }
            }
        }).start();
    }
}

开了四个线程,一个线程去跑a方法,a方法中调用b方法,a、b方法调用之前都有加锁,之后有解锁,这个线程用来做可重入锁的测试,其他三个线程是测试线程安全。

结果图

评论