자바 류 의 스 레 드 안전성 을 어떻게 테스트 합 니까?
스 레 드 안전성 은 자바 등 언어/플랫폼 의 중요 한 기준 으로 자바 에서 우 리 는 스 레 드 간 에 대상 을 공유 합 니 다.스 레 드 안전성 이 부족 해서 생 긴 문 제 는 디 버 깅 하기 어렵다.우발 적 이 고 목적지 재현 이 거의 불가능 하기 때문이다.어떻게 대상 을 테스트 하여 그것들 이 스 레 드 안전 을 확보 합 니까?
메모리 책장 이 있다 면
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()에 동기 화하 면 됩 니 다.아니면 똑똑 한 사이즈 의 동료 들,더 좋 은 방안 이 있 나 요?댓 글 을 환영 합 니 다.모두 함께 토론 합 시다.이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
작은 재료 : 결함 혼입, 테스트 레벨, 공정 책임결함은 후공정에서 적출할수록 비용이 부풀기 때문에 조기에 적출하는 것이 이상적입니다. 그럼에도 불구하고 결함의 종류에 따라 조기에 발견되는 것이나 후공정에서 처음으로 나타나게 되는 것이 있습니다. 예를 들어 컴파일러...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.