前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java Concurrent ReentrantLock(Java 10)

Java Concurrent ReentrantLock(Java 10)

作者头像
邹志全
发布2019-07-31 10:55:45
3750
发布2019-07-31 10:55:45
举报
文章被收录于专栏:EffectiveCoding

前言

Reentrant是一种可重入锁,是一种递归无阻塞的同步机制。实现了和synchronized类似的同步策略。与synchronized配合使用的wait、notify、notifyall等函数,由Codition负责提供,这个后续另外会说。

使用

1、在使用上与synchronized差异的是 实现lock接口的reentrant需要手动的去lock和release,因为synchronized是JVM也就是Java语法层面实现的,而Lock是JDK里面实现的。相对于sychronized来说,我们使用时要比synchronized更加严谨,因为忘记释放锁非常容易导致死锁。建议选择在finally中进行锁的释放 2、lock实现的锁粒度可以控制更加小 3、Lock 实现能更支持更多高级的特性,比如说锁超时等。 4、因为是JDK实现,所以具有了更多特性的高级锁比如说:read lock、write lock,并且支持我们自定义特殊的锁,这个虽然通常用不太到,但必要时是非常有用的。 demo:首先是使用synchronize,感受一下

代码语言:javascript
复制
class Test1 {
    private static volatile int condition = 0;
    private static final Object lock = new Object();
    public static void main(String[] args) throws InterruptedException {
        Thread A = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock) {
                    while (!(condition == 1)) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                        }
                    }
                    System.out.println("a executed by notify");
                }
            }
        });
        A.start();
        Thread.sleep(2000);
        condition = 1;
        synchronized (lock) {
            lock.notify();
        }
    }
}

然后是使用Lock

代码语言:javascript
复制
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Test2 {
        private static volatile int condition = 0;
        private static Lock lock = new ReentrantLock();
        private static Condition lockCondition = lock.newCondition();
        public static void main(String[] args) throws InterruptedException {
            Thread A = new Thread(new Runnable() {
                @Override
                public void run() {
                    lock.lock();
                    try {
                        while (!(condition == 1)) {
                            lockCondition.await();
                        }
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    } finally {
                        lock.unlock();
                    }
                    System.out.println("a executed by condition");
                }
            });
            A.start();
            Thread.sleep(2000);
            condition = 1;
            lock.lock();
            try {
                lockCondition.signal();
            } finally {
                lock.unlock();
            }
        }
}

原理

开始看源码: 首先可以看到ReentrantLock实现了LockSerializable 接口,序列化不多说,Lock定义了是现在JDK中锁的规范,然后类中持有一个Sync对象,其中Sync是ReentrantLock的一个静态内部类,这是整个ReentrantLock的基础,Sync继承自AQS,换句话说AQS也就是ReentrantLock的实现基础。

代码语言:javascript
复制
public class ReentrantLock implements Lock, java.io.Serializable {
    private static final long serialVersionUID = 7373984872572414699L;
    /** Synchronizer providing all implementation mechanics */
    private final Sync sync;
    /**
     * Base of synchronization control for this lock. Subclassed
     * into fair and nonfair versions below. Uses AQS state to
     * represent the number of holds on the lock.
     */
    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

然后往后看会得到另外几个静态内部类:FairSyncNonfairSync,他们均继承自Sync 这两个区别最大的操作是: 如果当前线程不是锁的占有者,则NonfairSync并不判断是否有等待队列,直接使用CAS去进行锁的占用; 如果当前线程不是锁的占有者,则FairSync则会判断当前是否有等待队列,如果有则将自己加到等待队列尾; 这其实就是公平锁&非公平锁的实现,默认非公平。 下面的代码中,我加了几行注释,大家注重关注下可重入及公平非公平是如何实现的。

代码语言:javascript
复制
/**
 * Sync object for non-fair locks
 */
static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

它这里使用的是Sync里面的实现:

代码语言:javascript
复制
abstract static class Sync extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = -5179523762034025860L;
    /**
     * Performs non-fair tryLock.  tryAcquire is implemented in
     * subclasses, but both need nonfair try for trylock method.
     */
    @ReservedStackAccess
    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;
}

然后是FairSync的实现

代码语言:javascript
复制
/**
 * Sync object for fair locks
 */
static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;
    /**
     * Fair version of tryAcquire.  Don't grant access unless
     * recursive call or no waiters or is first.
     */
    @ReservedStackAccess
    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;
    }
}

看到这里,大体已经能猜到具体实现了。ReentrantLock中维护着一个AQS,然后竞争锁的线程是在这里排队的,然后通过对应的CAS操作进行锁的争用。具体的实现参考AQS及CAS。 默认非公平:

代码语言:javascript
复制
/**
 * Creates an instance of {@code ReentrantLock}.
 * This is equivalent to using {@code ReentrantLock(false)}.
 */
public ReentrantLock() {
    sync = new NonfairSync();
}

可以很明显的看到底层实现几乎完全依赖于AQS,其实就是AQS包了一层罢了。

代码语言:javascript
复制
public void lock() {
    sync.acquire(1);
}
public void lockInterruptibly() throws InterruptedException {
    sync.acquireInterruptibly(1);
}
public boolean tryLock() {
    return sync.nonfairTryAcquire(1);
}
public boolean tryLock(long timeout, TimeUnit unit)
        throws InterruptedException {
    return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
public void unlock() {
    sync.release(1);
}
public int getHoldCount() {
    return sync.getHoldCount();
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019.07.17 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 使用
  • 原理
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档