4.3.1. Örnek: Delegasyonu Kullanan Araç İzleyici
Yetkilendirmenin daha önemli bir örneği olarak, iş parçacığı korumalı bir sınıfa delege eden araç izleyicisinin bir sürümünü oluşturalım. Konumları bir Haritada saklıyoruz, bu nedenle iş parçacığı açısından güvenli bir Harita uygulamasıyla başlıyoruz ConcurrentHashMap
. Konumu MutablePoint
, Liste 4.6'da gösterilen yerine değişmez bir Nokta sınıfı kullanarak da saklıyoruz .
4.6. DelegatingVehicleTracker tarafından kullanılan Immutable Point sınıfı.
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
değişmez olduğu için iş parçacığı açısından güvenlidir. Değişmez değerler özgürce paylaşılabilir ve yayınlanabilir, bu nedenle artık konumları iade ederken kopyalamamıza gerek kalmaz.
DelegatingVehicleTracker
Liste 4.7'de herhangi bir açık senkronizasyon kullanılmaz; duruma tüm erişim, tarafından yönetilir ConcurrentHashMap
ve Haritanın tüm anahtarları ve değerleri değişmezdir.
4.7 listesi. Bir ConcurrentHashMap'e İş Parçacığı Güvenliği Temsilcisi Veriliyor.
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 yerine orijinal sınıfı getLocations
kullansaydık, iş parçacığı için güvenli olmayan değiştirilebilir duruma bir referans yayınlayarak kapsüllemeyi bozuyor olurduk . Araç izleme sınıfının davranışını biraz değiştirdiğimize dikkat edin; monitör sürümü konumların anlık görüntüsünü döndürürken, yetkilendirme sürümü araç konumlarının değiştirilemez ancak "canlı" bir görünümünü döndürür. Bu, eğer A evresi çağırırsa getLocations
ve B iş parçacığı daha sonra bazı noktaların konumunu değiştirirse, bu değişiklikler A evresine döndürülen Haritaya yansıtılır.
4.3.2. Bağımsız Durum Değişkenleri
Temel durum değişkenleri bağımsız olduğu sürece, iş parçacığı güvenliğini birden fazla temel durum değişkenine devredebiliriz, bu da bileşik sınıfın birden çok durum değişkenini içeren herhangi bir değişmezi empoze etmediği anlamına gelir.
VisualComponent
Listing 4.9, istemcilerin fare ve tuş vuruşu olayları için dinleyicileri kaydetmesine olanak tanıyan bir grafik bileşendir. Her türden kayıtlı dinleyicilerin bir listesini tutar, böylece bir olay meydana geldiğinde uygun dinleyiciler çağrılabilir. Ancak fare dinleyicileri ve anahtar dinleyiciler arasında hiçbir ilişki yoktur; ikisi bağımsızdır ve bu nedenle VisualComponent
iş parçacığı güvenlik yükümlülüklerini temeldeki iki iş parçacığı güvenli listeye devredebilir.
4.9 listeleniyor. İş Parçacığı Güvenliğini Birden Çok Temel Durum Değişkenine Delege Etme.
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
her dinleyici listesini saklamak için a kullanır ; bu, özellikle dinleyici listelerini yönetmek için uygun olan iş parçacığı güvenli bir Liste uygulamasıdır (bkz. Bölüm 5.2.3). Her Liste iş parçacığı açısından güvenlidir ve birinin durumunu diğerinin durumuna bağlayan herhangi bir kısıtlama olmadığından VisualComponent
, iş parçacığı güvenlik sorumluluklarını temeldeki mouseListeners
ve keyListeners
nesnelere devredebilir .
4.3.3. Yetkilendirme Başarısız Olduğunda
Çoğu bileşik sınıf, VisualComponent
bileşen durum değişkenlerini ilişkilendiren değişmezlere sahiptirler. NumberRange
Listing 4.10 AtomicIntegers
, durumunu yönetmek için iki kullanır , ancak ek bir kısıtlama getirir - ilk sayının ikinciden küçük veya ona eşit olması.
4.10 listesi. Değişkenlerini Yeterince Korumayan Sayı Aralığı Sınıfı. Bunu yapma.
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
olan iş parçacığı güvenli değil ; alt ve üst sınırlayan değişmezi korumaz. setLower
Ve setUpper
yöntemler bu değişmeyen uygulamaya çalışırız ama çok kötü yapmak. Hem setLower
ve setUpper
check-sonra perdelik dizileri, ancak bunları atomik yapmak için yeterli kilitleme kullanmayın. Sayı aralığı (0, 10) tutarsa ve bir iş parçacığı çağırırken bir setLower(5)
başka iş parçacığı çağırırsa setUpper(4)
, bazı şanssız zamanlamalarla her ikisi de ayarlayıcılardaki kontrolleri geçecek ve her iki değişiklik de uygulanacaktır. Sonuç, aralığın artık (5, 4) değerini tutmasıdır - geçersiz bir durum . Dolayısıyla , temeldeki AtomicIntegers iş parçacığı açısından güvenliyken, bileşik sınıf değildir . Çünkü temeldeki durum değişkenleri lower
veupper
bağımsız NumberRange
değildir, basitçe iş parçacığı güvenliğini iş parçacığı güvenli durum değişkenlerine devredemez.
NumberRange
ortak bir kilitle alt ve üst koruma gibi değişmezlerini korumak için kilit kullanılarak vida güvenli hale getirilebilir. Müşterilerin değişmezlerini altüst etmesini önlemek için alt ve üst yayınlamaktan da kaçınmalıdır.
Bir sınıfta olduğu gibi bileşik eylemler varsa NumberRange
, yetkilendirme tek başına iş parçacığı güvenliği için uygun bir yaklaşım değildir. Bu durumlarda, bileşik eylemin tamamı temel durum değişkenlerine devredilemedikçe, bileşik eylemlerin atomik olmasını sağlamak için sınıf kendi kilitlemesini sağlamalıdır.
Bir sınıf, birden çok bağımsız iş parçacığı güvenli durum değişkeninden oluşuyorsa ve geçersiz durum geçişlerine sahip hiçbir işlem içermiyorsa, temel durum değişkenlerine iş parçacığı güvenliğini delege edebilir.