首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >对ReentrantReadWriteLock#tryLock失败的jc应激测试困惑

对ReentrantReadWriteLock#tryLock失败的jc应激测试困惑
EN

Stack Overflow用户
提问于 2018-09-25 15:57:11
回答 1查看 494关注 0票数 18

我正试着和JCStress打交道。为了确保我理解它,我决定为一些我知道必须正确的东西编写一些简单的测试:java.util.concurrent.locks.ReentrantReadWriteLock

我编写了一些非常简单的测试来检查锁模式的兼容性。不幸的是,有两个压力测试失败了:

  1. X_S: 正确的,真实的32,768个被禁止的没有默认情况提供,假设被禁止
  2. X_X: 正确的,真实的32,767个被禁止的没有默认情况提供,假设被禁止

在我看来,一个线程不应该持有读锁,而另一个线程也持有写锁。同样,两个线程不可能同时保持写锁。

我意识到,ReentrantReadWriteLock很可能不存在这个问题。我想,我可能是在jc应激测试中犯了一些愚蠢的错误,涉及JMM和读取锁的状态。

不幸的是,我找不到这个问题。有人能帮我理解一下(愚蠢的)吗?我犯的错误?

代码语言:javascript
复制
import org.openjdk.jcstress.annotations.*;
import org.openjdk.jcstress.infra.results.ZZ_Result;

import java.util.concurrent.locks.ReentrantReadWriteLock;

/*
 * |-----------------|
 * |  COMPATIBILITY  |
 * |-----------------|
 * |     | S   | X   |
 * |-----------------|
 * | S   | YES | NO  |
 * | X   | NO  | NO  |
 * |-----------------|
 */
public class ReentrantReadWriteLockBooleanCompatibilityTest {

    @State
    public static class S {
        public final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

        public boolean shared() {
            return lock.readLock().tryLock();
        }

        public boolean exclusive() {
            return lock.writeLock().tryLock();
        }
    }

    @JCStressTest
    @Outcome(id = "true, true", expect = Expect.ACCEPTABLE, desc = "T1 and T2 are both acquired S")
    public static class S_S {
        @Actor
        public void actor1(S s, ZZ_Result r) { r.r1 = s.shared(); }
        @Actor
        public void actor2(S s, ZZ_Result r) { r.r2 = s.shared(); }
    }

    @JCStressTest
    @Outcome(id = "true, false", expect = Expect.ACCEPTABLE, desc = "T1 acquired S, and T2 could not acquire X")
    @Outcome(id = "false, true", expect = Expect.ACCEPTABLE, desc = "T2 acquired X, and T1 could not acquire S")
    public static class S_X {
        @Actor
        public void actor1(S s, ZZ_Result r) { r.r1 = s.shared(); }
        @Actor
        public void actor2(S s, ZZ_Result r) { r.r2 = s.exclusive(); }
    }

    @JCStressTest
    @Outcome(id = "true, false", expect = Expect.ACCEPTABLE, desc = "T1 acquired X, and T2 could not acquire S")
    @Outcome(id = "false, true", expect = Expect.ACCEPTABLE, desc = "T2 acquired S and T1 could not acquire X")
    public static class X_S {
        @Actor
        public void actor1(S s, ZZ_Result r) { r.r1 = s.exclusive(); }
        @Actor
        public void actor2(S s, ZZ_Result r) { r.r2 = s.shared(); }
    }

    @JCStressTest
    @Outcome(id = "true, false", expect = Expect.ACCEPTABLE, desc = "T1 acquired X, and T2 could not acquire X")
    @Outcome(id = "false, true", expect = Expect.ACCEPTABLE, desc = "T2 acquired X and T1 could not acquire X")
    public static class X_X {
        @Actor
        public void actor1(S s, ZZ_Result r) { r.r1 = s.exclusive(); }
        @Actor
        public void actor2(S s, ZZ_Result r) { r.r2 = s.exclusive(); }
    }
}

我确实试着在jcstress-dev上询问这个问题,但没有收到任何回复-- http://mail.openjdk.java.net/pipermail/jcstress-dev/2018-August/000346.html。很抱歉交叉发帖,但我需要帮助,因此,我重新张贴到StackOverflow,希望得到更多的观众关注。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-10-01 02:22:35

您的测试通过时,运行时对jcpression0.3。在0.4版本中,行为更改为包含在启动时运行的正常检查的结果(请参见针对bug 此承诺jcstress忽略了在健康检查期间收集的样本。)。

一些正常检查在单个线程中运行,您的测试不处理由同一个线程调用两个参与者的情况;您正在测试一个可重入的锁,因此,如果写锁已经持有,那么读锁就会通过。

这可以说是jcstress中的一个bug,因为@Actor上的文档说不变量是:

  • 每个方法只由一个特定的线程调用。
  • 每个State实例准确地调用每个方法一次。

虽然文档的措辞不那么清晰,但是生成的源代码明确表示,其意图是在自己的线程中运行每个参与者。

解决这一问题的一种方法是允许通过单线程情况:

代码语言:javascript
复制
@State
public static class S {
    public final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    public boolean shared() {
        return lock.readLock().tryLock();
    }

    public boolean exclusive() {
        return lock.writeLock().tryLock();
    }

    public boolean locked() {
        return lock.isWriteLockedByCurrentThread();
    }
}

@JCStressTest
@Outcome(id = "true, false, false", expect = Expect.ACCEPTABLE, desc = "T1 acquired X, and T2 could not acquire S")
@Outcome(id = "false, false, true", expect = Expect.ACCEPTABLE, desc = "T2 acquired S and T1 could not acquire X")
@Outcome(id = "true, true, true", expect = Expect.ACCEPTABLE, desc = "T1 acquired X and then acquired S")
public static class X_S {
    @Actor
    public void actor1(S s, ZZZ_Result r) {
        r.r1 = s.exclusive();
    }
    @Actor
    public void actor2(S s, ZZZ_Result r) {
        r.r2 = s.locked();
        r.r3 = s.shared();
    }
}

或者检查单个线程的情况,并将其标记为“有趣”,而不是接受:

代码语言:javascript
复制
@State
public static class S {
    public final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    public AtomicReference<Thread> firstThread = new AtomicReference<>();

    public boolean shared() {
        firstThread.compareAndSet(null, Thread.currentThread());
        return lock.readLock().tryLock();
    }

    public boolean exclusive() {
        firstThread.compareAndSet(null, Thread.currentThread());
        return lock.writeLock().tryLock();
    }

    public boolean sameThread() {
        return Thread.currentThread().equals(firstThread.get());
    }

    public boolean locked() {
        return lock.isWriteLockedByCurrentThread();
    }
}

@JCStressTest
@Outcome(id = "false, true, false, false", expect = Expect.ACCEPTABLE, desc = "T1 acquired X, and T2 could not acquire X")
@Outcome(id = "false, false, false, true", expect = Expect.ACCEPTABLE, desc = "T2 acquired X and T1 could not acquire X")
@Outcome(id = "false, true, true, true", expect = Expect.ACCEPTABLE_INTERESTING, desc = "Both actors ran in the same thread!")
@Outcome(id = "true, true, false, true", expect = Expect.ACCEPTABLE_INTERESTING, desc = "Both actors ran in the same thread!")
public static class X_X {
    @Actor
    public void actor1(S s, ZZZZ_Result r) {
        r.r1 = s.sameThread();
        r.r2 = s.exclusive();
    }
    @Actor
    public void actor2(S s, ZZZZ_Result r) {
        r.r3 = s.sameThread();
        r.r4 = s.exclusive();
    }
}

正如您在注释中所指出的,上述测试中的最终@Outcome从未发生过。这是因为单线程性检查在运行参与者之前不会洗牌(请参阅生成的测试类上的sanityCheck_Footprints方法)。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/52502306

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档