JUnit5 병렬 실행

테스트 병렬 실행 설정 방법, 클래스 기본 병렬 실행 모드 변경, 베타적 제어, 액세스 모드 지정 방법 소개

테스트 병렬 실행

기본적으로 모든 테스트 메소드는 단일 스레드에서 순차적으로 실행된다. 여기에서는 병렬로 실행하는 방법에 대해서 소개해 보도록 하겠다.

테스트 병렬 실행하도록 설정

테스트를 병렬로 실행하기 위해서는 먼저 설정 파일이 필요하다.

junit-platform.properties 라는 설정 파일을 클래스 패스 루트에 생성한다.
그러고, 아래와 같이 junit.jupiter.execution.parallel.enabled=true라고 설정하면 병렬 실행이 활성화 된다. /test/resource/junit-platform.properties

junit.jupiter.execution.parallel.enabled=true

이 설정만으로는 테스트 메소드는 변함없이 단일 thread로 순차적으로 실행된다.

테스트 메소드를 병렬로 실행하려면 테스트 클래스에 @Execution 어노테이션을 설정하고, valueCONCURRENT으로 지정해야 한다.

SAME_THREAD를 지정하면 부모와 동일한 스레드에서 실행된다. 기본값은 이것으로 설정되어 있다.

package com.devkuma.junit5.parallel;

import org.junit.jupiter.api.*;
import org.junit.jupiter.api.parallel.Execution;
import org.junit.jupiter.api.parallel.ExecutionMode;

@Execution(ExecutionMode.CONCURRENT)
public class ParallelTest1 {
    @BeforeAll
    static void beforeAll() {
        printThread("beforeAll()");
    }

    @BeforeEach
    void beforeEach(TestInfo testInfo) {
        String name = testInfo.getDisplayName();
        printThread("  " + name + ":beforeEach()");
    }

    @Test
    void test1() {
        printThread("    test1()");
    }

    @Test
    void test2() {
        printThread("    test2()");
    }

    @AfterEach
    void afterEach(TestInfo testInfo) {
        String name = testInfo.getDisplayName();
        printThread("  " + name + ":afterEach()");
    }

    @AfterAll
    static void afterAll() {
        printThread("afterAll()");
    }

    private static void printThread(String test) {
        String name = Thread.currentThread().getName();
        System.out.printf("%s@%s%n", test, name);
    }
}

실행 결과:

beforeAll()@ForkJoinPool-1-worker-19
  test2():beforeEach()@ForkJoinPool-1-worker-23
  test1():beforeEach()@ForkJoinPool-1-worker-5
    test2()@ForkJoinPool-1-worker-23
    test1()@ForkJoinPool-1-worker-5
  test1():afterEach()@ForkJoinPool-1-worker-5
  test2():afterEach()@ForkJoinPool-1-worker-23
afterAll()@ForkJoinPool-1-worker-19

기본 실행 모드 변경

설정 파일에 junit.jupiter.execution.parallel.mode.default=concurrent으로 지정하면 기본 실행 모드가 병렬 실행(ExecutionMode.CONCURRENT을 지정한 상태)이 된다.

/test/resource/junit-platform.properties

junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.mode.default=concurrent

클래스 파일에서는 @Execution 어노테이션을 지정하지 않았다.

package com.devkuma.junit5.parallel;

import org.junit.jupiter.api.Test;

public class ParallelTest2 {

    @Test
    void test1() {
        printThread("test1()");
    }

    @Test
    void test2() {
        printThread("test2()");
    }

    private static void printThread(String test) {
        String name = Thread.currentThread().getName();
        System.out.printf("%s@%s%n", test, name);
    }
}

실행 결과:

test2()@ForkJoinPool-1-worker-23
test1()@ForkJoinPool-1-worker-5

기본 실행 모드가 적용되지 않는 예외

junit.jupiter.execution.parallel.mode.default=concurrent를 지정하면 기본으로 대부분의 테스트 메소드가 병렬로 실행되게 된다.

단, 예외로서 아래와 같이 설정된 테스트 클래스나 메소드는 디폴트의 실행 모드를 변경해도 병렬 실행되지 않게 된다.

  • Lifecycle.PER_CLASS로 지정한 테스트 클래스
  • MethodOrderer.Random외에 다른 MethodOrderer가 지정된 테스트 메소드
package com.devkuma.junit5.parallel;

import org.junit.jupiter.api.*;

public class ParallelTest3 {

    @Nested
    class StandardNestedTest {

        @Test
        void test1() {
            printThread("StandardNestedTest.test1()");
        }

        @Test
        void test2() {
            printThread("StandardNestedTest.test2()");
        }
    }

    @Nested
    @TestInstance(TestInstance.Lifecycle.PER_CLASS)
    class PerClassTest {

        @Test
        void test1() {
            printThread("PerClassTest.test1()");
        }

        @Test
        void test2() {
            printThread("PerClassTest.test2()");
        }
    }

    @Nested
    @TestMethodOrder(MethodOrderer.MethodName.class)
    class OrderedTest {

        @Test
        void test1() {
            printThread("OrderedTest.test1()");
        }

        @Test
        void test2() {
            printThread("OrderedTest.test2()");
        }
    }

    private static void printThread(String test) {
        String name = Thread.currentThread().getName();
        System.out.printf("%s@%s%n", test, name);
    }
}

실행 결과:

OrderedTest.test1()@ForkJoinPool-1-worker-5
PerClassTest.test1()@ForkJoinPool-1-worker-23
StandardNestedTest.test1()@ForkJoinPool-1-worker-27
StandardNestedTest.test2()@ForkJoinPool-1-worker-19
PerClassTest.test2()@ForkJoinPool-1-worker-23
OrderedTest.test2()@ForkJoinPool-1-worker-5

위에 예제에서 StandardNestedTest의 테스트 메소드는 다른 thread에서 실행되고 있다(thread명이 다르다). 그러나, PerClassTest와의 OrderedTest 테스트 메소드는 각각 다른 thread로 실행되고 있다(thread명이 같다).

명시적으로 병렬 실행 지정

@TestMethodOrder으로 일부러 순서를 지정하고 있는 테스트를 병렬 실행시킨다는 것은 이상한 애기긴 하지만, 이러한 클래스의 테스트 메소드도 병렬 실행시키고 싶은 경우는 그 테스트 클래스가 thread 세이프인 것을 확인한 후에, 명시적으로 @Execution(ExecutionMode.CONCURRENT)를 지정한다.

package com.devkuma.junit5.parallel;

import org.junit.jupiter.api.*;

class ParallelTest3 {

    @Nested
    class StandardNestedTest {
      // ...
    }

    @Nested
    @TestInstance(TestInstance.Lifecycle.PER_CLASS)
    @Execution(ExecutionMode.CONCURRENT)
    class PerClassTest {
      // ...
    }

    @Nested
    @TestMethodOrder(MethodOrderer.Alphanumeric.class)
    @Execution(ExecutionMode.CONCURRENT)
    class OrderedTest {
      // ...
    }

    private static void printThread(String test) {...}
}

실행 결과:

PerClassTest.test2()@ForkJoinPool-1-worker-23
StandardNestedTest.test2()@ForkJoinPool-1-worker-9
StandardNestedTest.test1()@ForkJoinPool-1-worker-27
OrderedTest.test2()@ForkJoinPool-1-worker-5
PerClassTest.test1()@ForkJoinPool-1-worker-31
OrderedTest.test1()@ForkJoinPool-1-worker-13

최상위 클래스의 기본 실행 모드 변경

설정 파일에 junit.jupiter.execution.parallel.mode.classes.default=concurrent으로 지정하면 최상위 클래스의 기본 실행 모드만 변경이 된다.

/test/resource/junit-platform.properties

junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.mode.classes.default=concurrent

앞에서설정했던 junit.jupiter.execution.parallel.mode.default=concurrent것은 메소드 레벨의 병렬 실행을 제어하는 ​​것으로, 이번에는 그 설정을 넣지 않았다.
따라서, 기본으로 same_thread를 지정하고 있는 것과 같은 상태가 된다.

두개의 테스트 클래스를 생성하고, 두 클래스를 한번에 실행해 보도록 하자.

package com.devkuma.junit5.parallel;

import org.junit.jupiter.api.Test;

public class ParallelFooTest {
    
    @Test
    void test1() {
        printThread("ParallelFooTest.test1()");
    }

    @Test
    void test2() {
        printThread("ParallelFooTest.test2()");
    }

    private static void printThread(String test) {
        String name = Thread.currentThread().getName();
        System.out.printf("%s@%s%n", test, name);
    }
}
package com.devkuma.junit5.parallel;

import org.junit.jupiter.api.Test;

public class ParallelBarTest {

    @Test
    void test1() {
        printThread("ParallelBarTest.test1()");
    }

    @Test
    void test2() {
        printThread("ParallelBarTest.test2()");
    }

    private static void printThread(String test) {
        String name = Thread.currentThread().getName();
        System.out.printf("%s@%s%n", test, name);
    }
}

실행 결과:

ParallelFooTest.test2()@ForkJoinPool-1-worker-19
ParallelBarTest.test1()@ForkJoinPool-1-worker-5
ParallelFooTest.test1()@ForkJoinPool-1-worker-19
ParallelBarTest.test2()@ForkJoinPool-1-worker-5

위에 결과를 보면 테스트 클래스 단위로 다른 thread 로 실행되었고, 테스트 클래스내의 메소드는 단일 thread로 순차 실행되었다.

  • ParallelFooTest, ParallelBarTest는 다른 스레드(ForkJoinPool-1-worker-19, ForkJoinPool-1-worker-5)에서 실행되었다.
  • 그러나 ParallelFooTest 내에 각 메소드는 하나의 스레드(ForkJoinPool-1-worker-19)에서 실행되었다.

설정별 Thread 실행

junit.jupiter.execution.parallel.mode.defaultjunit.jupiter.execution.parallel.mode.classes.default 설정의 조합은 전부 4가지 있고 각각 다음과 같은 동작이 된다.

클래스, 메소드 단일 스레드로 차례로 실행

junit.jupiter.execution.parallel.mode.default=same_thread
junit.jupiter.execution.parallel.mode.classes.default=same_thread

병렬 실행

클래스별로 병렬 실행, 클래스 내의 메소드 차례로 실행

junit.jupiter.execution.parallel.mode.default=same_thread
junit.jupiter.execution.parallel.mode.classes.default=concurrent
junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.mode.classes.default=concurrent

병렬 실행

클래스 내의 메소드는 병렬 실행, 클래스는 차례로 실행

junit.jupiter.execution.parallel.mode.default=concurrent
junit.jupiter.execution.parallel.mode.classes.default=same_thread

병렬 실행

클래스, 메소드 전부 병렬 실행

junit.jupiter.execution.parallel.mode.default=concurrent
junit.jupiter.execution.parallel.mode.classes.default=concurrent

병렬 실행

동시 병렬 수 변경

프로세서(코어) 수에 따라 동시 병렬 수를 동적으로 변경

설정 구성 파일에서 junit.jupiter.execution.parallel.config.dynamic.factor를 지정하여 병렬로 실행되는 스레드 수를 조정할 수 있다.
/test/resource/junit-platform.properties

jjunit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.config.strategy=dynamic
junit.jupiter.execution.parallel.mode.default=concurrent
junit.jupiter.execution.parallel.config.dynamic.factor=2

아래 클래스는 동시 실행 중인 스레드 수를 계산하는 클래스이다.

package com.devkuma.junit5.parallel;

import java.util.concurrent.atomic.AtomicInteger;

public class ParallelismCounter {
    private final AtomicInteger counter = new AtomicInteger(0);
    private final AtomicInteger max = new AtomicInteger(0);

    public void increment() {
        this.max.set(Math.max(this.max.get(), this.counter.incrementAndGet()));
    }

    public void decrement() {
        this.counter.decrementAndGet();
    }

    public int getMaxCount() {
        return this.max.get();
    }
}

아래 테스트 코드는 동적 테스트 메커니즘을 사용하여 처리가 1초가 걸리는 테스트를 20개 실행되도록 하고, 테스트가 끝난 후(@AfterEach)에 다양한 정보가 출력하게 된다.

package com.devkuma.junit5.parallel;

import org.junit.jupiter.api.*;

import java.util.stream.IntStream;
import java.util.stream.Stream;

public class ParallelismTest {
    private long begin;
    private ParallelismCounter counter = new ParallelismCounter();

    @BeforeEach
    void beforeEach() {
        begin = System.currentTimeMillis();
    }

    @TestFactory
    Stream<DynamicNode> testFactory() {
        return IntStream
                .range(0, 20)
                .mapToObj(i -> DynamicTest.dynamicTest("test" + i, () -> {
                    counter.increment();
                    Thread.sleep(1000);
                    counter.decrement();
                }));
    }

    @AfterEach
    void printThreadNames() {
        System.out.println(System.currentTimeMillis() - begin + "ms");
        System.out.println("Available Processors = " + Runtime.getRuntime().availableProcessors());
        System.out.println("Max Parallelism Count = " + counter.getMaxCount());
        System.out.println("Active Thread Count = " + Thread.activeCount());
        Thread[] activeThreads = new Thread[Thread.activeCount()];
        Thread.enumerate(activeThreads);
        IntStream.range(0, activeThreads.length)
                .mapToObj(i -> "[" + i + "] " + activeThreads[i].getName())
                .forEach(System.out::println);
    }
}

실행 결과:

1023ms
Available Processors = 10
Max Parallelism Count = 20
Active Thread Count = 25
[0] Test worker
[1] /127.0.0.1:49609 to /127.0.0.1:49608 workers
[2] /127.0.0.1:49609 to /127.0.0.1:49608 workers Thread 2
[3] /127.0.0.1:49609 to /127.0.0.1:49608 workers Thread 3
[4] ForkJoinPool-1-worker-51
[5] ForkJoinPool-1-worker-37
[6] ForkJoinPool-1-worker-23
[7] ForkJoinPool-1-worker-9
[8] ForkJoinPool-1-worker-59
[9] ForkJoinPool-1-worker-45
[10] ForkJoinPool-1-worker-31
[11] ForkJoinPool-1-worker-17
[12] ForkJoinPool-1-worker-3
[13] ForkJoinPool-1-worker-53
[14] ForkJoinPool-1-worker-39
[15] ForkJoinPool-1-worker-25
[16] ForkJoinPool-1-worker-11
[17] ForkJoinPool-1-worker-61
[18] ForkJoinPool-1-worker-47
[19] ForkJoinPool-1-worker-33
[20] ForkJoinPool-1-worker-19
[21] ForkJoinPool-1-worker-5
[22] ForkJoinPool-1-worker-55
[23] ForkJoinPool-1-worker-41
[24] ForkJoinPool-1-worker-27
  • factor에 지정한 값에 실행 환경의 프로세서 수(코어 수)를 곱한 수가, 병렬 실행되는 thread 수가 된다.
    • 테스트를 진행한 컴퓨터의 코어 수는 10이고, factor를 2로 지정하였으므로, 병렬로 실행되는 thread 수는 20이 된다.
    • 그리고, 병렬 실행의 실행에는 ForkJoinPool이 사용되고 있어, 풀 되는 thread 수는 병렬 실행되는 thread수 보다 많아지는 일이 있다 (Active thread 수가 병렬 실행 수보다 많다.)
  • factor를 지정하지 않으면 기본값은 1이다.

factor를 소수로 지정
그러고, factor은 내부에서 BigDecimal를 사용하기에, 소수를 지정할 수도 있다.

  • thread 수는 구하고 발생한 수에서 소수점 이하는 버림(마지막으로 BigDecimal.intValue()를 사용하여 int로 변환된다).

/test/resource/junit-platform.properties

junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.config.strategy=dynamic
junit.jupiter.execution.parallel.mode.default=concurrent
junit.jupiter.execution.parallel.config.dynamic.factor=0.5

실행 결과:

7063ms
Available Processors = 10
Max Parallelism Count = 3
Active Thread Count = 7
[0] Test worker
[1] /127.0.0.1:50150 to /127.0.0.1:50149 workers
[2] /127.0.0.1:50150 to /127.0.0.1:50149 workers Thread 2
[3] /127.0.0.1:50150 to /127.0.0.1:50149 workers Thread 3
[4] ForkJoinPool-1-worker-3
[5] ForkJoinPool-1-worker-5
[6] ForkJoinPool-1-worker-7

테스트를 진행한 컴퓨터의 코어 수는 10이고, factor를 0.5로 지정하였으므로, 계산해 보면 병렬로 실행되는 thread 수는 5가 된다.

동시 병렬 수를 고정 값으로 설정

junit.jupiter.execution.parallel.config.strategy로 fixed지정하면 고정 병렬 수를 지정할 수 있다.
병렬 수는 junit.jupiter.execution.parallel.config.fixed.parallelism로 지정한다.

/test/resource/junit-platform.properties

junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.config.strategy=fixed
junit.jupiter.execution.parallel.mode.default=concurrent
junit.jupiter.execution.parallel.config.fixed.parallelism=6

테스트 코드 구현은 dynamic을 지정했을 때와 동일하다.

실행 결과:

4046ms
Available Processors = 10
Max Parallelism Count = 6
Active Thread Count = 10
[0] Test worker
[1] /127.0.0.1:50251 to /127.0.0.1:50250 workers
[2] /127.0.0.1:50251 to /127.0.0.1:50250 workers Thread 2
[3] /127.0.0.1:50251 to /127.0.0.1:50250 workers Thread 3
[4] ForkJoinPool-1-worker-3
[5] ForkJoinPool-1-worker-5
[6] ForkJoinPool-1-worker-7
[7] ForkJoinPool-1-worker-9
[8] ForkJoinPool-1-worker-11
[9] ForkJoinPool-1-worker-13

### 동시 병렬 수를 임의로 사용자 정의 junit-jupiter-engine는 디폴트으로 런타임만 참조할 수 있으므로, 컴파일시도 참조할 수 있도록(testImplementation) 지정한다.

build.gradle

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
    //testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
    testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
}

ParallelExecutionConfigurationStrategy 구현한 클래스 생성하다.

package com.devkuma.junit5.parallel;

import org.junit.platform.engine.ConfigurationParameters;
import org.junit.platform.engine.support.hierarchical.ParallelExecutionConfiguration;
import org.junit.platform.engine.support.hierarchical.ParallelExecutionConfigurationStrategy;

public class MyParallelExecutionConfigurationStrategy implements ParallelExecutionConfigurationStrategy {

    @Override
    public ParallelExecutionConfiguration createConfiguration(ConfigurationParameters configurationParameters) {
        return new ParallelExecutionConfiguration() {

            @Override
            public int getParallelism() {
                return 7;
            }

            @Override
            public int getMinimumRunnable() {
                return 7;
            }

            @Override
            public int getMaxPoolSize() {
                return 7;
            }

            @Override
            public int getCorePoolSize() {
                return 7;
            }

            @Override
            public int getKeepAliveSeconds() {
                return 30;
            }
        };
    }
}
  • createConfiguration() 메서드에서 ParallelExecutionConfiguration구현한 인스턴스를 반환한다.
  • ParallelExecutionConfiguration에 정의된 GetterForkJoinPool을 생성 시 사용된다.

/test/resource/junit-platform.properties

junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.config.strategy=custom
junit.jupiter.execution.parallel.mode.default=concurrent
junit.jupiter.execution.parallel.config.custom.class=sample.junit5.MyParallelExecutionConfigurationStrategy
  • junit.jupiter.execution.parallel.config.strategy에 custom 지정
  • junit.jupiter.execution.parallel.config.custom.class에서 새로 만든 ParallelExecutionConfigurationStrategy 구현 클래스을 지정한다.

테스트 코드 구현은 dynamic을 지정했을 때와 동일하다.

실행 결과:

3043ms
Available Processors = 10
Max Parallelism Count = 7
Active Thread Count = 11
[0] Test worker
[1] /127.0.0.1:50725 to /127.0.0.1:50724 workers
[2] /127.0.0.1:50725 to /127.0.0.1:50724 workers Thread 2
[3] /127.0.0.1:50725 to /127.0.0.1:50724 workers Thread 3
[4] ForkJoinPool-1-worker-3
[5] ForkJoinPool-1-worker-5
[6] ForkJoinPool-1-worker-7
[7] ForkJoinPool-1-worker-9
[8] ForkJoinPool-1-worker-11
[9] ForkJoinPool-1-worker-13
[10] ForkJoinPool-1-worker-15
  • MyParallelExecutionConfigurationStrategy에서 반환 된 ParallelExecutionConfiguration 설정에 따라 병렬로 실행되는 것을 확인할 수 있다.

배타적 제어

배타적 제어(Exclusive control)란? 공유 자원에 대한 다중 액세스가 동시에 발생해도 문제 없이 동작할 수 있는 제어를 말한다.

배타적 제어가 없는 경우

클래스를 병렬로 실행하여 static 변수 n를 세 가지 테스트 방법에서 10,000번 증가시킨다.

package com.devkuma.junit5.parallel;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;

public class ExclusiveControlTest {
    static int n = 0;

    @Test
    void test1() throws Exception {
        process("test1");
    }

    @Test
    void test2() throws Exception {
        process("test2");
    }

    @Test
    void test3() throws Exception {
        process("test3");
    }

    private void process(String name) {
        System.out.println("begin " + name);
        for (int i = 0; i < 10000; i++) {
            n++;
        }
        System.out.println("end " + name);
    }

    @AfterAll
    static void afterAll() {
        System.out.println("n = " + n);
    }
}

실행 결과:

begin test3
begin test2
begin test1
end test3
end test2
end test1
n = 15978

당연히 동기화가 없으므로 결과는 30,000이 아니다.

배타적 제어를 넣은 경우

방법은 간단하다 각 테스트 메소드에 @ResourceLock 어노테이션을 설정한다.

package com.devkuma.junit5.parallel;

...

public class ExclusiveControlTest {
    static int n = 0;

    @Test
    @ResourceLock("lock")
    void test1() throws Exception { ... }

    @Test
    @ResourceLock("lock")
    void test2() throws Exception { ... }

    @Test
    @ResourceLock("lock")
    void test3() throws Exception { ... }

    private void process(String name) { ... }

    @AfterAll
    static void afterAll() { ... }
}

실행 결과:

begin test1
end test1
begin test2
end test2
begin test3
end test3
n = 30000
  • 각 메소드의 실행으로 동기화가 되어, n의 값은 30,000 이 되었다.
  • @ResourceLock을 클래스나 메소드로 설정하면, 그 안의 테스트 케이스는 동기를 실행하게 된다.
  • value에는 배타적 제어하기 위한 키가 되는 문자열을 지정한다.
    • 동일한 키 문자열이 설정된 @ResourceLock에 대해서만 동기화가 발생한다.

액세스 모드 지정

package com.devkuma.junit5.parallel;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.parallel.Execution;
import org.junit.jupiter.api.parallel.ExecutionMode;
import org.junit.jupiter.api.parallel.ResourceAccessMode;
import org.junit.jupiter.api.parallel.ResourceLock;

@Execution(ExecutionMode.CONCURRENT)
public class ExclusiveControlTest2 {

    @Test
    @ResourceLock(value = "lock", mode = ResourceAccessMode.READ_WRITE)
    void test1() throws Exception {
        process("test1(READ_WRITE)");
    }

    @Test
    @ResourceLock(value = "lock", mode = ResourceAccessMode.READ)
    void test2() throws Exception {
        process("test2(READ)");
    }

    @Test
    @ResourceLock(value = "lock", mode = ResourceAccessMode.READ)
    void test3() throws Exception {
        process("test3(READ)");
    }

    private void process(String name) throws Exception {
        System.out.println("begin " + name);
        Thread.sleep(500);
        System.out.println("end " + name);
    }
}

실행 결과:

begin test3(READ)
begin test2(READ)
end test3(READ)
end test2(READ)
begin test1(READ_WRITE)
end test1(READ_WRITE)

@ResourceLockmode에 의해 액세스 모드를 지정할 수 있다.

  • 액세스 모드는 ResourceAccessMode에 정의된 상수로 지정한다.

각 액세스 모드에 의한 배타 제어의 조합은 다음과 같다.

선행하고 있는 처리 나중에 발생한 처리 나중에 발생한 처리는
READ READ 실행 가능
READ READ_WRITE 대기
READ_WRITE READ 대기
READ_WRITE READ_WRITE 대기
  • READ만 있다면 병렬 실행 가능하지만, READ_WRITE가 있다면 배타적 제어된다.
  • READ는 데이터를 읽을 때만 업데이트하지 않는 테스트 방법으로 지정하고, READ_WRITE는 데이터를 업데이트하는 테스트 메소드를 지정하는 방법이다.



최종 수정 : 2022-12-19