java多线程学习(5)——Lock锁初探

首先,比较下java两种线程锁:synchronized锁与Lock锁:
1.synchronized
优点:实现简单,语义清晰,便于JVM堆栈跟踪,加锁解锁过程由JVM自动控制,提供了多种优化方案,使用更广泛
缺点:悲观的排他锁,不能进行高级功能
2.lock
优点:可定时的、可轮询的与可中断的锁获取操作,提供了读写锁、公平锁和非公平锁,可实现更细粒度的锁,可以知道当前线程锁定状态  
缺点:需手动释放锁unlock,不适合JVM进行堆栈跟踪
3.相同点 
都是可重入锁


Lock锁有很多不同的类型,但是其实现都依赖AbstractQueuedSynchronizer类(简称AQS),这个类可以称的上是所有Lock锁的鼻祖。AQS是一个队列同步器,在AbstractQueuedSynchronizer维护一个队列,采用CAS操作更新该队列,当线程无法获取锁的时,会将该线程构造成一个node,然后添加至同步队列尾部。队列中的每一个阻塞线程通过队列中的其node的preNode唤醒,也就是,当队列中第一个排队的线程执行完毕之后,会唤醒第二个阻塞的线程,以此类推。
由于所有的Lock锁都以AQS为基础,实现思路都大同小异,本文以ReentrantLock作为讲解。

//ReentrantLock类实现Lock接口
public class ReentrantLock implements Lock, java.io.Serializable {
    private final Sync sync;
    //此类中Lea大神定义一个抽象的Sync继承AQS类,是因为ReenTrantLock提供公平锁和非公平锁。
    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

        /**
         * Performs {@link Lock#lock}. The main reason for subclassing
         * is to allow fast path for nonfair version.
         */
        abstract void lock();

        /**
         * Performs non-fair tryLock.  tryAcquire is implemented in
         * subclasses, but both need nonfair try for trylock method.
         */
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

        protected final boolean isHeldExclusively() {
            // While we must in general read state before owner,
            // we don't need to do so to check if current thread is owner
            return getExclusiveOwnerThread() == Thread.currentThread();
        }

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

        // Methods relayed from outer class

        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }

        final int getHoldCount() {
            return isHeldExclusively() ? getState() : 0;
        }

        final boolean isLocked() {
            return getState() != 0;
        }

        /**
         * Reconstitutes the instance from a stream (that is, deserializes it).
         */
        private void readObject(java.io.ObjectInputStream s)
            throws java.io.IOException, ClassNotFoundException {
            s.defaultReadObject();
            setState(0); // reset to unlocked state
        }
    }
}
......
//公共的方法,直接面向使用者,实际上调用的是sync的lock方法
public void lock() {
        sync.lock();
    }
public void lockInterruptibly() throws InterruptedException {
    sync.acquireInterruptibly(1);
}
......

在ReentrantLock类中,定义了一个抽象的Sync类,该抽象类重写了AQS的tryRelease()方法,另外,有两个内部类继承Sync抽象类,NonfairSync和FairSync,分别是用来实现公平锁和非公平锁。在这两个Sync子类中重写了AQS的tryAcquire()方法。

//非公平锁
static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }
//公平锁
static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            acquire(1);
        }

        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }

我们以公平锁FairSync来分析:
1. 首先调用lock()方法,执行acquire(1),acquire()方法为AQS实现的方法
调用tryAcquire()方法(注意,该方法被ReenTrantLock类重写),如果返回true代表该线程可以立即执行,acquire()方法执行结束,调用lock()方法的线程可正常往下执行;如果返回false,代表未获取到同步资源,此线程阻塞,然后调用acquireQueued()方法,由于该方法里边有一个死循环,直到满足某个条件,所以从某种程度上说这个死循环相当于一个自旋锁,让该线程处于一种阻塞的状态;当acquireQueued()方法返回false时,表示当前线程之前的所有的并发线程已经执行完成,当前线程已经是可执行状态,若返回false,表示当前线程被中断或者取消,此时if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))条件成立,将会执行 selfInterrupt()方法将该线程中断。

public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
//该方法利用for循环不断的检测当前node的状态,直到该node的前驱结点执行,或者当前node被中断或取消
final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
//将当前node加入到同步队列的尾部
private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }

总结
方法调用过程:
锁定:
lock() ——> acquire() ——> tryAcquire()(需要被我们自己重写)——> addWaiter() (tryAcquire()不满足,将当前线程加入到AQS同步队列中)——> acquireQueued()(从AQS队列中循环获取当前线程的阻塞状态)
解锁:
unLock() ——> release() ——>tryRelease()(需要被我们自己重写)——>AQS同步队列头结点出队列

通过以上分析,实际上Lock锁就是通过不断的循环AQS的同步队列,判断当前线程所在结点的前驱结点是否已经出队列,如果已经出队列,那么for循环终止,该死循环模拟了线程阻塞的过程。
**关键的部分:Sync类重写tryAcquire(),tryeRelease()方法,这两个方法分别对应
当前线程结点是否满足AQS队列入队和出队条件,当tryAcquire()返回false,表示当前线程需要被阻塞,即加入到AQS同步队列中,开始执行死循环(模拟阻塞状态);当在线程中执行unlock()方法时,在AQS中对应的操作即是从同步队列中出队列一个结点,并且唤醒下一个结点,该结点对应的线程结束死循环,变成可执行状态。**

以上

参考:https://www.cnblogs.com/jiangds/p/6476293.html

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值