Смешанный экземпляр и статический синхронизированный метод, получают неожиданные результаты тестирования - PullRequest
0 голосов
/ 03 января 2019

Насколько мне известно, в java статический синхронизированный метод и метод синхронизации экземпляров не будут влиять на выполнение друг друга, поскольку они блокируются на другом объекте (экземпляр this против самого объекта class).

В следующем коде есть 2 подпотока, один запускает метод синхронизации экземпляров, а другой - статический синхронизированный метод.

Поскольку оператор ++ не является атомарным, я ожидаю, что следующий контрольный пример пройдет (окончательное число должно быть меньше, чем время ++ вызова) , но оно всегда не пройдёт тест (окончательный счет равен времени ++ вызвано) .

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

Из выходных данных вы можете видеть, что 2 потока действительно смешаны, но конечное число не меньше, даже после увеличения ROUND до 1 миллиона оно остается прежним.

Итак, в какой части я ошибаюсь?

1 Ответ

0 голосов
/ 03 января 2019

System.out.printf является внутренним значением synchronized, хотя оно не гарантирует неудачного теста, оно может иметь влияние.

Вы можете попробовать удалить их, чтобы устранить помехи, например:

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

И это прошло некоторое время на моем компьютере:

final count: 1902
final count: 1111
final count: 1883
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...