代码之家  ›  专栏  ›  技术社区  ›  Eric

混合实例和静态同步方法,获得意外的测试输出

  •  3
  • Eric  · 技术社区  · 7 年前

    据我所知,在java中,静态同步&实例同步方法不会影响彼此的执行,因为它们锁定在不同的对象上( this 实例vs class 对象本身)。

    在下面的代码中有两个子线程,一个运行实例同步方法,另一个运行静态同步方法。

    ++ 运算符不是原子的,我希望下面的测试用例能够通过 ++ 被叫) ,但它总是无法通过测试 (最终计数等于时间 ++ 被叫) .

    SyncInstanceAndStaticRelationshipLearn.java

    import org.testng.Assert;
    import org.testng.annotations.Test;
    
    /**
     * Relationship of instance & static synchronized method.
     *
     * @author eric
     * @date 1/3/19 9:32 PM
     */
    public class SyncInstanceAndStaticRelationshipLearn {
        private static final int ROUND = 1000;
        private static final int INC_THREAD_COUNT = 2;
        private static final long OPTIONAL_INC_DELAY = 1; // optional increase delay,
        private static int N = 0;
    
        @Test
        public void test() throws InterruptedException {
            ThreadGroup tg = new ThreadGroup("runner");
    
            new Thread(tg, () -> {
                try {
                    new MixedCounter().batchInsSync();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, "ts-inst").start();
    
            new Thread(tg, () -> {
                try {
                    MixedCounter.batchStaticSync();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, "ts-static").start();
    
            Thread[] tArr = new Thread[INC_THREAD_COUNT];
            tg.enumerate(tArr); // get threads,
    
            // wait all runner to finish,
            for (Thread t : tArr) {
                t.join();
            }
    
            System.out.printf("\nfinal count: %d\n", getN());
            // just check the output, and can see the instance & static methods mixed,
            Assert.assertTrue(getN() < INC_THREAD_COUNT * ROUND);
        }
    
        public static int getN() {
            return N;
        }
    
        // increase & print,
        private static void incAndPrint() throws InterruptedException {
            System.out.printf("[%s] start, N: %d\n", Thread.currentThread().getName(), getN());
            N++;
            Thread.sleep(OPTIONAL_INC_DELAY);
            System.out.printf("[%s] end, N: %d\n", Thread.currentThread().getName(), getN());
        }
    
        // batch increase & print,
        private static void batchInsAndPrint() throws InterruptedException {
            for (int i = 0; i < ROUND; i++) {
                incAndPrint();
            }
        }
    
        // mixed instance / static counter,
        static class MixedCounter {
            public synchronized void batchInsSync() throws InterruptedException {
                batchInsAndPrint();
            }
    
            public synchronized static void batchStaticSync() throws InterruptedException {
                batchInsAndPrint();
            }
        }
    }
    

    [ts-inst] start, N: 0
    [ts-static] start, N: 0
    [ts-inst] end, N: 1
    [ts-inst] start, N: 2
    [ts-inst] end, N: 3
    [ts-inst] start, N: 3
    [ts-static] end, N: 2
    [ts-inst] end, N: 4
    [ts-inst] start, N: 4
    [ts-inst] end, N: 5
    [ts-inst] start, N: 5
    [ts-inst] end, N: 6
    [ts-inst] start, N: 6
    [ts-inst] end, N: 7
    [ts-inst] start, N: 7
    [ts-inst] end, N: 8
    [ts-inst] start, N: 8
    [ts-static] start, N: 4
    [ts-inst] end, N: 9
    [ts-inst] start, N: 10
    [ts-inst] end, N: 11
    [ts-inst] start, N: 11
    [ts-static] end, N: 10
    ...
    [ts-inst] start, N: 1999
    [ts-inst] end, N: 2000
    
    final count: 2000
    
    java.lang.AssertionError: expected [true] but found [false]
    Expected :true
    Actual   :false
    

    从输出中,您可以看到这两个线程确实是混合的,但即使在增加了 ROUND

    那么,我错在哪一部分呢?

    1 回复  |  直到 7 年前
        1
  •  1
  •   xingbin    7 年前

    System.out.printf 是 synchronized 在内部,虽然它不能保证测试失败,但它可能会产生影响。

    您可以尝试删除它们以消除干扰,例如:

    private static void incAndPrint() throws InterruptedException {  
        N++; 
    }
    

    这在我的电脑上通过了一段时间的测试:

    final count: 1902
    final count: 1111
    final count: 1883