前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java ReentrantLock锁的公平性与非公平性

Java ReentrantLock锁的公平性与非公平性

作者头像
夹胡碰
发布2020-11-24 12:37:22
7060
发布2020-11-24 12:37:22
举报
文章被收录于专栏:程序猿~程序猿~

锁按照公平性划分为公平锁非公平锁,在Java中,ReentrantLock有这两种锁的具体实现,下文进行展示。

说明

以食堂打饭的场景举例说明

1. 公平锁

想要获取锁资源的线程在队列里进行排队等待,新来的线程去队尾排队,锁资源释放的时候只有队首线程可以获得锁资源。

  • 排队等待

image.png

2. 非公平锁

新来的线程直接和队首线程争抢锁资源,如果争抢到了,则直接获取锁资源,队首线程继续等待。 如果新来的线程竞争失败,则去队尾进行排队,只能等待队列前所有线程执行完毕后自己才能获取锁。

注意: 锁的非公平性只在首次和队首线程进行锁竞争时有体现,竞争失败入列后则与公平锁执行方式一致

  • 插队成功

image.png

  • 插队失败

image.png

重点代码

1. 初始化默认为非公平锁,也可以设置为公平锁
代码语言:javascript
复制
public ReentrantLock() {
    // 默认为非公平锁
    sync = new NonfairSync();
}

public ReentrantLock(boolean fair) {
    // 可以设置为公平锁
    sync = fair ? new FairSync() : new NonfairSync();
}
2. 入列之前lock方法中尝试插队获取锁
  • 公平锁排队获取锁
代码语言:javascript
复制
static final class FairSync extends Sync {
    final void lock() {
         // 排队获取锁,如果没获取到就排队
        acquire(1);
    }
}
  • 非公平锁入列之前首次尝试获取锁
代码语言:javascript
复制
static final class NonfairSync extends Sync {
    final void lock() {
        // 插队尝试获取锁(第一次尝试)
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
        // 排队获取锁,如果没获取到就排队
            acquire(1);
    }
}
3. tryAcquire时第二次尝试插队获取锁资源
  • 公平锁
代码语言:javascript
复制
static final class FairSync extends Sync {
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
        // hasQueuedPredecessors() 表示前面是队列中是否有线程在排队(true-有、false-没有)
        // 这段的逻辑是队列中没有线程排队了,才能获取锁,如果前面有线程排队,即使锁资源刚好释放,
        // 队首线程没有及时获取到锁,被当前线程获取到了,也不允许当前线程拥有锁
        // 公平锁就要保证新来的线程始终到队尾排队
            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;
    }
}
  • 非公平锁入列之前第二次尝试获取锁
代码语言:javascript
复制
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // 相比公平锁没有hasQueuedPredecessors() ,说明不管队列中有没有排队的线程,
        // 只要能获取到锁资源,锁资源就交给新来的线程。
        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;
}
4. 两次都没有插队获取到所资源,则入列变成公平获取锁的一员
代码语言:javascript
复制
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        // acquireQueued() 入列等待
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

意义

ReentrantLock默认使用非公平锁的意义是:

非公平锁减少了线程切换带来的上下文切换,牺牲了公平性但是提高了性能。 提供公平锁是对锁的获取顺序进行了保证,牺牲了部分性能。

参考

  1. 看完你就明白的锁系列之锁的公平性
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 说明
  • 重点代码
  • 意义
  • 参考
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档