4.3.1. 예 : 위임을 사용하는 차량 추적기
위임의보다 실질적인 예로서 스레드로부터 안전한 클래스에 위임하는 차량 추적기의 버전을 구성 해 보겠습니다. 지도에 위치를 저장하므로 스레드로부터 안전한지도 구현 인 ConcurrentHashMap
. 또한 MutablePoint
Listing 4.6에 표시된 것처럼 대신 불변의 Point 클래스를 사용하여 위치를 저장합니다 .
목록 4.6. DelegatingVehicleTracker에서 사용하는 변경 불가능한 Point 클래스입니다.
class Point{
public final int x, y;
public Point() {
this.x=0; this.y=0;
}
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
Point
불변이기 때문에 스레드로부터 안전합니다. 변경 불가능한 값은 자유롭게 공유하고 게시 할 수 있으므로 더 이상 반환 할 때 위치를 복사 할 필요가 없습니다.
DelegatingVehicleTracker
목록 4.7에서는 명시 적 동기화를 사용하지 않습니다. 상태에 대한 모든 액세스는에서 관리 ConcurrentHashMap
하며지도의 모든 키와 값은 변경할 수 없습니다.
목록 4.7. 스레드 안전성을 ConcurrentHashMap에 위임.
public class DelegatingVehicleTracker {
private final ConcurrentMap<String, Point> locations;
private final Map<String, Point> unmodifiableMap;
public DelegatingVehicleTracker(Map<String, Point> points) {
this.locations = new ConcurrentHashMap<String, Point>(points);
this.unmodifiableMap = Collections.unmodifiableMap(locations);
}
public Map<String, Point> getLocations(){
return this.unmodifiableMap; // User cannot update point(x,y) as Point is immutable
}
public Point getLocation(String id) {
return locations.get(id);
}
public void setLocation(String id, int x, int y) {
if(locations.replace(id, new Point(x, y)) == null) {
throw new IllegalArgumentException("invalid vehicle name: " + id);
}
}
}
MutablePoint
Point 대신 원래 클래스를 사용했다면 getLocations
스레드로부터 안전하지 않은 변경 가능한 상태에 대한 참조를 게시 하도록하여 캡슐화를 깨뜨릴 것 입니다. 차량 추적기 클래스의 동작을 약간 변경했습니다. 모니터 버전은 위치의 스냅 샷을 반환하는 반면 위임 버전은 수정 불가능하지만 차량 위치의 "실시간"보기를 반환합니다. 즉, 스레드 A가 호출 getLocations
하고 스레드 B가 나중에 일부 지점의 위치를 수정하면 해당 변경 사항이 스레드 A에 반환 된 맵에 반영됩니다.
4.3.2. 독립 상태 변수
또한 기본 상태 변수가 독립적 인 한 둘 이상의 기본 상태 변수에 스레드 안전성을 위임 할 수 있습니다. 즉, 복합 클래스가 여러 상태 변수를 포함하는 불변성을 부과하지 않습니다.
VisualComponent
목록 4.9에서 클라이언트가 마우스 및 키 입력 이벤트에 대한 리스너를 등록 할 수 있도록하는 그래픽 구성 요소입니다. 이벤트가 발생하면 적절한 리스너를 호출 할 수 있도록 각 유형의 등록 된 리스너 목록을 유지합니다. 그러나 마우스 리스너 세트와 키 리스너 간에는 관계가 없습니다. 둘은 독립적이므로 VisualComponent
스레드 안전 의무를 두 개의 기본 스레드 안전 목록에 위임 할 수 있습니다.
목록 4.9. 스레드 안전성을 여러 기본 상태 변수에 위임.
public class VisualComponent {
private final List<KeyListener> keyListeners
= new CopyOnWriteArrayList<KeyListener>();
private final List<MouseListener> mouseListeners
= new CopyOnWriteArrayList<MouseListener>();
public void addKeyListener(KeyListener listener) {
keyListeners.add(listener);
}
public void addMouseListener(MouseListener listener) {
mouseListeners.add(listener);
}
public void removeKeyListener(KeyListener listener) {
keyListeners.remove(listener);
}
public void removeMouseListener(MouseListener listener) {
mouseListeners.remove(listener);
}
}
VisualComponent
를 사용하여 CopyOnWriteArrayList
각 리스너 목록을 저장합니다. 이것은 리스너 목록 관리에 특히 적합한 스레드로부터 안전한 목록 구현입니다 (섹션 5.2.3 참조). 각 목록은 스레드로부터 안전하며 하나의 상태를 다른 상태에 연결하는 제약 조건이 없기 때문에 VisualComponent
스레드 안전 책임을 기본 mouseListeners
및 keyListeners
개체에 위임 할 수 있습니다 .
4.3.3. 위임이 실패 할 때
대부분의 복합 클래스는 VisualComponent
구성 요소 상태 변수와 관련된 불변성이 있습니다. NumberRange
Listing 4.10에서는 AtomicIntegers
상태를 관리하기 위해 2 개 를 사용 하지만 첫 번째 숫자가 두 번째 숫자보다 작거나 같다는 추가 제약을 부과합니다.
목록 4.10. 불변을 충분히 보호하지 않는 숫자 범위 클래스. 이러지마
public class NumberRange {
// INVARIANT: lower <= upper
private final AtomicInteger lower = new AtomicInteger(0);
private final AtomicInteger upper = new AtomicInteger(0);
public void setLower(int i) {
//Warning - unsafe check-then-act
if(i > upper.get()) {
throw new IllegalArgumentException(
"Can't set lower to " + i + " > upper ");
}
lower.set(i);
}
public void setUpper(int i) {
//Warning - unsafe check-then-act
if(i < lower.get()) {
throw new IllegalArgumentException(
"Can't set upper to " + i + " < lower ");
}
upper.set(i);
}
public boolean isInRange(int i){
return (i >= lower.get() && i <= upper.get());
}
}
NumberRange
입니다 스레드 안전하지 ; 그것은 하부와 상부를 구속하는 불변성을 보존하지 않습니다. setLower
및 setUpper
방법이 불변을 존중하려고하지만, 그렇게 가난하게. setLower
및 둘 다 setUpper
check-then-act 시퀀스이지만 원 자성을 만들기 위해 충분한 잠금을 사용하지 않습니다. 숫자 범위가 (0, 10)이고 한 스레드가을 호출 setLower(5)
하는 동안 다른 스레드가를 호출하는 경우 setUpper(4)
, 불행한 타이밍으로 둘 다 setter의 검사를 통과하고 두 수정 사항이 모두 적용됩니다. 그 결과 범위는 이제 유효하지 않은 상태 인 (5, 4)를 유지 합니다. 따라서 기본 AtomicInteger는 스레드로부터 안전하지만 복합 클래스는 . 기본 상태 변수 lower
와upper
독립적 NumberRange
이지 않으며 단순히 스레드 안전성을 스레드 안전성 상태 변수에 위임 할 수 없습니다.
NumberRange
잠금을 사용하여 고정을 유지함으로써 스레드로부터 안전하도록 만들 수 있습니다 (예 : 공통 잠금으로 하단 및 상단 보호). 또한 클라이언트가 불변성을 파괴하는 것을 방지하기 위해 하위 및 상위 게시를 피해야합니다.
클래스에 복합 동작 NumberRange
이있는 경우 위임만으로는 스레드 안전성에 적합한 접근 방식이 아닙니다. 이러한 경우 클래스는 전체 복합 동작이 기본 상태 변수에 위임 될 수있는 경우를 제외하고 복합 동작이 원자적임을 보장하기 위해 자체 잠금을 제공해야합니다.
클래스가 여러 개의 독립적 인 스레드 안전 상태 변수로 구성되고 유효하지 않은 상태 전환이있는 작업이없는 경우 스레드 안전을 기본 상태 변수에 위임 할 수 있습니다.