测试 Java 方法是否同步的好方法是什么?

我有几个实现一些接口的类。该接口有一个协定,即某些方法应该同步,有些方法不应同步,我想通过所有实现的单元测试来验证该协定。这些方法应使用 sync 关键字或被锁定 - 与 syncedCollection() 包装器非常相似。这意味着我应该能够在外部观察它。this

为了继续 Collections.synchronizedCollection() 的例子,如果我有一个线程调用 iterator(),我仍然应该能够使用另一个线程进入像 add() 这样的方法,因为 iterator() 不应该做任何锁定。另一方面,我应该能够在外部同步集合,并看到add()上的另一个线程块。

有没有一种好方法来测试方法是否在JUnit测试中同步?我想避免长时间的睡眠陈述。


答案 1

如果你只是想检查一个方法是否具有修饰符,除了明显的(查看源代码/ Javadoc),你也可以使用反射。synchronized

Modifier.isSynchronized(method.getModifiers())

测试方法是否保证在所有并发方案中正确同步的更一般问题可能是一个不可判定的问题。


答案 2

这些都是可怕的想法,但你可以这样做...

1

    // Substitute this LOCK with your monitor (could be you object you are
    // testing etc.)
    final Object LOCK = new Object();
    Thread locker = new Thread() {
        @Override
        public void run() {
            synchronized (LOCK) {
                try {
                    Thread.sleep(Long.MAX_VALUE);
                } catch (InterruptedException e) {
                    System.out.println("Interrupted.");
                    return;
                }
            }
        }
    };

    locker.start();

    Thread attempt = new Thread() {
        @Override
        public void run() {
            // Do your test.
        }
    };

    attempt.start();
    try {
        long longEnough = 3000 * 1000;// It's in nano seconds

        long before = System.nanoTime();
        attempt.join(longEnough);
        long after = System.nanoTime();

        if (after - before < longEnough) {
            throw new AssertionError("FAIL");
        } else {
            System.out.println("PASS");
        }
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        return;
    }
    locker.interrupt();

2

如果您知道参数上的方法总是在任何实现中调用,则可以传递一个伪装成参数并调用 holdsLock() 的模拟对象。

所以像:

class Mock implements Argument {
    private final Object LOCK;
    private final Argument real;
    public Mock(Object obj, Argument real){
       this.LOCK=obj;
       this.real = real;
    }

    @Overrides
    public void something(){
        System.out.println("held:"+Thread.holdsLock(LOCK));
        this.real.something();
    }

然后等待类在 Argument 上调用 something()。


推荐