자바 류 의 스 레 드 안전성 을 어떻게 테스트 합 니까?

이 글 은 자바 류 의 스 레 드 안전성 을 어떻게 테스트 하 는 지 를 소개 하 였 으 며,예시 코드 를 통 해 매우 상세 하 게 소개 하 였 으 며,여러분 의 학습 이나 업무 에 대해 어느 정도 참고 학습 가 치 를 가지 고 있 으 며,필요 한 친 구 는 참고 할 수 있 습 니 다.
스 레 드 안전성 은 자바 등 언어/플랫폼 의 중요 한 기준 으로 자바 에서 우 리 는 스 레 드 간 에 대상 을 공유 합 니 다.스 레 드 안전성 이 부족 해서 생 긴 문 제 는 디 버 깅 하기 어렵다.우발 적 이 고 목적지 재현 이 거의 불가능 하기 때문이다.어떻게 대상 을 테스트 하여 그것들 이 스 레 드 안전 을 확보 합 니까?
메모리 책장 이 있다 면

package com.mzc.common.thread;
 
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
 
/**
 * <p class="detail">
 *   :     
 * </p>
 *
 * @author Moore
 * @ClassName Books.
 * @Version V1.0.
 * @date 2019.12.10 14:00:13
 */
public class Books {
 final Map<Integer, String> map = new ConcurrentHashMap<>();
 
 /**
 * <p class="detail">
 *   :   ,     id
 * </p>
 *
 * @param title :
 * @return int
 * @author Moore
 * @date 2019.12.10 14:00:16
 */
 int add(String title) {
 final Integer next = this.map.size() + 1;
 this.map.put(next, title);
 return next;
 }
 
 /**
 * <p class="detail">
 *   :     id    
 * </p>
 *
 * @param id :
 * @return string
 * @author Moore
 * @date 2019.12.10 14:00:16
 */
 String title(int id) {
 return this.map.get(id);
 }
}
우선,우 리 는 책 한 권 을 책장 에 넣 으 면 책장 은 그것 의 ID 로 돌아 갈 것 이다.그리고 우 리 는 그것 의 ID 를 통 해 책 이름 을 읽 을 수 있 습 니 다.이렇게:

Books books = new Books();
String title = "Elegant Objects";
int id = books.add(title);
assert books.title(id).equals(title);
이것 은 스 레 드 안전 과 유사 합 니 다.왜냐하면 우 리 는 스 레 드 안전 한 Concurrent HashMap 을 사용 하기 때 문 입 니 다.더 원시 적 이 고 비 스 레 드 안전 한 HashMap 이 아 닙 니 다.그 렇 죠?우리 먼저 테스트 해 보 자.

public class BooksTest {
 @Test
 public void addsAndRetrieves() {
 Books books = new Books();
 String title = "Elegant Objects";
 int id = books.add(title);
 assert books.title(id).equals(title);
 }
}
테스트 결과 보기:

테스트 는 통과 되 었 지만,이것 은 단지 단일 라인 테스트 일 뿐이다.몇 개의 병렬 스 레 드 에서 같은 조작 을 시도 해 봅 시다(저 는 Hamcrest 를 사용 합 니 다).

/**
 * <p class="detail">
 *   :      
 * </p>
 *
 * @throws ExecutionException the execution exception
 * @throws InterruptedException the interrupted exception
 * @author Moore
 * @date 2019.12.10 14:16:34
 */
 @Test
 public void addsAndRetrieves2() throws ExecutionException, InterruptedException {
 Books books = new Books();
 int threads = 10;
 ExecutorService service = Executors.newFixedThreadPool(threads);
 Collection<Future<Integer>> futures = new ArrayList<>(threads);
 for (int t = 0; t < threads; ++t) {
  final String title = String.format("Book #%d", t);
  futures.add(service.submit(() -> books.add(title)));
 }
 Set<Integer> ids = new HashSet<>();
 for (Future<Integer> f : futures) {
  ids.add(f.get());
 }
 assertThat(ids.size(), equalTo(threads));
 }
우선,나 는 실행 프로그램 을 통 해 스 레 드 풀 을 만 들 었 다.그리고 나 서 나 는 Submit()를 통 해 10 개의 Callable 유형의 대상 을 제출 했다.그들 은 모두 책꽂이 에 유일한 새 책 한 권 을 추가 할 것 이다.이 모든 것 은 풀 의 10 개의 스 레 드 중 일부 스 레 드 에서 예측 할 수 없 는 순서 로 실 행 될 것 이다.
그리고 Future 형식의 대상 목록 을 통 해 실행 자의 결 과 를 얻 었 습 니 다.마지막 으로,나 는 만 든 유일한 도서 ID 의 수량 을 계산한다.숫자 가 10 이면 충돌 이 없다.ID 목록 에 유일한 요소 만 포함 되 어 있 는 지 확인 하기 위해 set 집합 을 사용 합 니 다.
우 리 는 이렇게 개 조 된 운행 결 과 를 살 펴 보 자.

테스트 도 통 과 했 지만 강하 지 않 았 다.문 제 는 여러 병렬 스 레 드 에서 이 책 들 을 테스트 하지 않 았 다 는 점 이다.commt()를 두 번 호출 하 는 데 걸 리 는 시간 이 충분 하여 books.add()의 실행 을 완성 할 수 있 습 니 다.이것 이 바로 실제로 하나의 스 레 드 만 동시에 운행 할 수 있 는 이유 이다.
우 리 는 코드 를 수정 해서 다시 그것 을 검사 할 수 있다.

@Test
 public void addsAndRetrieves3() {
  Books books = new Books();
  int threads = 10;
  ExecutorService service = Executors.newFixedThreadPool(threads);
  AtomicBoolean running = new AtomicBoolean();
  AtomicInteger overlaps = new AtomicInteger();
  Collection<Future<Integer>> futures = new ArrayList<>(threads);
  for (int t = 0; t < threads; ++t) {
   final String title = String.format("Book #%d", t);
   futures.add(
     service.submit(
       () -> {
        if (running.get()) {
         overlaps.incrementAndGet();
        }
        running.set(true);
        int id = books.add(title);
        running.set(false);
        return id;
       }
     )
   );
  }
  assertThat(overlaps.get(), greaterThan(0));
 }
테스트 결과 보기:

실행 오류,삽 입 된 책 과 되 돌아 오 는 id 수량 이 충돌 하지 않 음 을 설명 합 니 다.
위의 코드 를 통 해 나 는 스 레 드 간 의 중첩 주파수 와 병행 실행 빈 도 를 알 아 보 려 고 했다.그러나 기본적으로 확률 이 0 이기 때문에 이 테스트 는 내 가 측정 하고 싶 은 것 을 진정 으로 측정 하지 못 했다.아직 우리 가 원 하 는 것 이 아니 라 단지 열 권 의 책 을 한 권 한 권 씩 책꽂이 에 넣 었 을 뿐이다.
다시:

내 가 스 레 드 수 를 1000 으로 늘 리 면 겹 치 거나 병행 하기 시작 하 는 것 을 볼 수 있다.
하지만 스 레 드 수가 10 개 밖 에 없 을 때 도 겹 쳐 병행 하 는 경우 가 있 기 를 바란다.어 떡 하지?이 문 제 를 해결 하기 위해 서 저 는 CountDownlatch 를 사용 합 니 다.

@Test
  public void addsAndRetrieves4() throws ExecutionException, InterruptedException {
    Books books = new Books();
    int threads = 10;
    ExecutorService service = Executors.newFixedThreadPool(threads);
    CountDownLatch latch = new CountDownLatch(1);
    AtomicBoolean running = new AtomicBoolean();
    AtomicInteger overlaps = new AtomicInteger();
    Collection<Future<Integer>> futures = new ArrayList<>(threads);
    for (int t = 0; t < threads; ++t) {
      final String title = String.format("Book #%d", t);
      futures.add(
          service.submit(
              () -> {
                latch.await();
                if (running.get()) {
                  overlaps.incrementAndGet();
                }
                running.set(true);
                int id = books.add(title);
                running.set(false);
                return id;
              }
          )
      );
    }
    latch.countDown();
    Set<Integer> ids = new HashSet<>();
    for (Future<Integer> f : futures) {
      ids.add(f.get());
    }
    assertThat(overlaps.get(), greaterThan(0));
  }
현재 모든 스 레 드 는 책 에 접근 하기 전에 잠 금 권한 을 기 다 려 야 합 니 다.우리 가 Submit()를 통 해 모든 내용 을 제출 할 때,그것들 은 보류 하고 기다 릴 것 이다.그리고 나 서 우 리 는 countDown()으로 자 물 쇠 를 풀 고 동시에 실행 하기 시작 했다.
실행 결과 보기:

실행 결 과 를 통 해 알 수 있 듯 이 현재 스 레 드 수 는 10 이지 만 스 레 드 의 중첩 수 는 0 보다 많 기 때문에 assertTrue 가 실행 을 통 과 했 고 ids 도 10 과 같 지 않다.즉,예전 처럼 10 개의 도서 ID 를 얻 지 못 했다.분명히 Books 류 는 스 레 드 가 안전 하지 않 습 니 다!
이 종 류 를 최적화 하기 전에 테스트 를 간소화 하 는 방법 을 알려 드 리 겠 습 니 다.Cactoos 에서 온 RunInThreads 를 사용 하 는 것 은 우리 가 위 에서 한 것 과 똑 같 지만 코드 는 이 렇 습 니 다.

@Test
  public void addsAndRetrieves5() {
    Books books = new Books();
    MatcherAssert.assertThat(
        t -> {
          String title = String.format(
              "Book #%d", t.getAndIncrement()
          );
          int id = books.add(title);
          return books.title(id).equals(title);
        },
        new RunsInThreads<>(new AtomicInteger(), 10)
    );
  }
assertThat()의 첫 번 째 매개 변 수 는 Func(함수 인터페이스)의 인 스 턴 스 입 니 다.AtomicInteger(Runs Threads 의 첫 번 째 매개 변수)를 받 아들 이 고 불 값 을 되 돌려 줍 니 다.이 함 수 는 10 개의 병렬 스 레 드 에서 실 행 됩 니 다.위 와 같은 잠 금 기반 방법 을 사용 합 니 다.
이 RunIn Threads 는 보기에 매우 치밀 하고 사용 하기에 도 편리 하 므 로 여러분 께 추천 합 니 다.사용 할 수 있 습 니 다.프로젝트 에 의존 도 를 추가 하기 만 하면 됩 니 다:

<dependency>
      <groupId>org.llorllale</groupId>
      <artifactId>cactoos-matchers</artifactId>
      <version>0.18</version>
    </dependency>
마지막 으로 Books 류 를 스 레 드 안전 으로 만 들 기 위해 서 는 그 방법 add()에 동기 화하 면 됩 니 다.아니면 똑똑 한 사이즈 의 동료 들,더 좋 은 방안 이 있 나 요?댓 글 을 환영 합 니 다.모두 함께 토론 합 시다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기