Java’da Fail-Fast ve Fail-Safe

Bayram EKER
3 min readFeb 2, 2023

--

Fail-Fast sistemleri, işlemi olabildiğince hızlı bir şekilde durdurarak hataları anında ortaya çıkarır ve tüm işlemi durdurur.

Fail-Safe sistemleri bir hata durumunda işlemi iptal etmez. Bu tür sistemler, hataları mümkün olduğunca ortaya çıkarmaktan kaçınır.

Fail-Fast olayında yineleyiciler(iterators) doğrudan koleksiyonun kendisinde çalışırken Fail-Safe de ise klonlanmış bir kopyasında çalışır ve temel fark burada ortaya çıkar.

Eşzamanlı Değişiklik

Java’daki Eşzamanlı değişiklik, bir nesne üzerinde başka bir görev çalışırken eşzamanlı olarak değişiklik yapmaktır. Basit bir ifadeyle, eşzamanlı değişiklik, başka bir iş parçacığı üzerlerinde çalışırken nesneleri değiştirme işlemidir. Koleksiyondaki öğelerin değerini kaldırarak, ekleyerek veya güncelleyerek veri koleksiyonunun yapısını değiştirir.

Tüm yineleyiciler bu davranışı desteklemez; bazı yineleyicileri kullanırken ConcurrentModificationException’ dönebilir.

Fail Fast

Yineleyiciler doğrudan koleksiyonlar üzerinde çalıştığı için yineleme sırasında, hızlı yineleyiciler, koleksiyonun değiştirildiğini fark ettikleri anda (yani, bir üyenin eklendiğini, değiştirildiğini veya kaldırıldığını fark ettiklerinde) başarısız olur ve bir ConcurrentModificationException ile dönerler.

Koleksiyonun yapısal olarak değiştirilmesi durumunda hemen ConcurrentModificationException hatası döner. Yapısal değişiklik, bir veri koleksiyonundaki bir öğenin değerini eklemek, kaldırmak, güncellemek anlamına gelirken, başka bir iş parçacığı bu koleksiyon üzerinde yinelenir. Fail Fast yineleyici örnekleri, ArrayList ve HashMap koleksiyon sınıflarındaki yineleyicilerdir.

Fail Fast yineleyicimiz koleksiyonun yapısal olarak değiştirilip değiştirilmediğini bilmek için modCount adlı bir dahili bayrak kullanır. modCount bir koleksiyon her değiştirildiğinde güncellenir; sonraki değeri kontrol eder; bulursa, bu yineleyici oluşturulduktan sonra modCount değiştirilecektir. ConcurrentModificationException hatası dönecektir.

import java.util.HashMap;   
import java.util.Iterator;
import java.util.Map;
public class FailFastDemo {
public static void main(String[] args)
{
Map<String, String> empName = new HashMap<String, String>();
empName.put("Sam Hanks", "New york");
empName.put("Will Smith", "LA");
empName.put("Scarlett", "Chicago");
Iterator iterator = empName.keySet().iterator();
while (iterator.hasNext()) {
System.out.println(empName.get(iterator.next()));
empName.put("Istanbul", "Turkey");
}
}
}

Çıktı:

LA
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextNode(HashMap.java:1445)
at java.util.HashMap$KeyIterator.next(HashMap.java:1469)
at FailFastDemo.main(FailFastDemo.java:14)

Yukarıdaki örnekten çıkarım yapacak olursak

  • Fail Fast yineleyici, üzerinde yineleme yapılırken bir koleksiyon değiştirilirse bir ConcurrentModificationException atar.
  • Fail Fast yineleyici, koleksiyonun öğeleri üzerinde gezinmek için orijinal bir koleksiyonu kullanır.
  • Hafıza koruyuculardır, fazladan hafıza gerektirmezler.
  • ArrayList, HashMap, Vector sınıfları tarafından döndürülen Fail Fast yineleyicileridir.

Fail-safe

Fail Safe yineleyicileri, Fail Fast yineleyicilerinin tam tersidir; onlardan farklı olarak, arıza korumalı bir yineleyici, yineleme işlemi sırasında koleksiyon değiştirilirse işleyemediği sürece herhangi bir özel durum oluşturmaz. Bu, orijinal nesne yerine koleksiyon nesnesinin kopyası üzerinde çalıştıkları için yapılabilir. Orijinal koleksiyon üzerinde gerçekleştirilen yapısal değişiklikler onlar tarafından yok sayılır ve orijinal koleksiyonu değil kopyalanan koleksiyonu etkiler. Böylece, orijinal koleksiyon yapısal olarak değişmeden tutulacaktır.

Bu yineleyiciler, gerçek Koleksiyonun bir klonunu oluşturur ve üzerinde çalışır. Yineleyici oluşturulduktan sonra herhangi bir değişiklik olursa, aslına dokunulmadan değişiklik yapılır. Bu nedenle, bu Yineleyiciler , değiştirilmiş olsa bile Koleksiyon üzerinde döngü yapmaya devam eder.

Tam anlamıyla “hata korumalı” yani “fail safe” diye bir şeyin olmadığını zayıf bir yapıya sahip olduğunu unutmayalım.

Bir örnekle inceleyelim

import java.util.concurrent.CopyOnWriteArrayList;   
import java.util.Iterator;
class FailSafeDemo {
public static void main(String args[])
{
CopyOnWriteArrayList<Integer> list
= new CopyOnWriteArrayList<Integer>(new Integer[] { 1, 7, 9, 11 });
Iterator itr = list.iterator();
while (itr.hasNext()) {
Integer i = (Integer)itr.next();
System.out.println(i);
if (i == 7)
list.add(15);
}
}
}

Çıktı:

1
7
9
11

Yukarıdaki örnekten, diğer iş parçacığı sonucu yazdırırken koleksiyonun yinelendiğini görebiliriz. Çıktı, diğer işlemden etkilenmez; bu, koleksiyonun ayrı bir kopyasının oluşturulduğu ve yinelemenin bunun üzerinden gerçekleştirildiği anlamına gelir.

Ancak, Fail Fast yineleyiciyi kullanmayan bir koleksiyonun, ConcurrentModificationException ‘dan kaçınmak için bellekte bir klonu veya kopyası oluşturması gerekli değildir . Örneğin, ConcurrentHashMap , başarısız olmamasına rağmen, bir nesnenin ayrı bir kopyası üzerinde çalışmaz. Bunun yerine, belirtim tarafından hatasız hızlı yineleme olarak belirtilen semantiği kullanır.

Aşağıdaki örneği göz önünde bulundurun:

FailSafeDemo1.java:

import java.util.concurrent.ConcurrentHashMap;   
import java.util.Iterator;
public class FailSafeDemo1 {
public static void main(String[] args)
{

ConcurrentHashMap<String, Integer> m
= new ConcurrentHashMap<String, Integer>();
m.put("Bir", 1);
m.put("İki", 7);
m.put("Beş", 5);
m.put("Sekiz", 8);

Iterator it = m.keySet().iterator();
while (it.hasNext()) {
String key = (String)it.next();
System.out.println(key + " : " + m.get(key));
m.put("Dokuz", 9);
}
}
}

Çıktı:

Sekiz : 8
Beş : 5
Dokuz : 9
Bir : 1
Yedi : 7

Yukarıdaki örnekte, iş parçacığı çalışırken koleksiyonu yinelediğimizi görebiliriz. Yineleme sonucu aynı koleksiyona yerleştirilir; bu, nesnenin herhangi bir ayrı kopyasını oluşturmadığı ve ayrıca herhangi bir ConcurrentModificationException oluşturmadığı anlamına gelir.

Yukarıdaki örneklerden, Fail Safe yineleyicileri hakkında aşağıdaki çıkarımları yapabiliriz:

  • İterasyon işlemi yaparken bir koleksiyon üzerinde modifikasyon işlemlerini gerçekleştirebiliriz.
  • Yineleme sırasında ConcurrentModificationException dönmez.
  • Fail Safe yineleyicileri, öğeler arasında geçiş yapmak için koleksiyonun bir kopyasını kullanır.
  • Fail Fast’in aksine, koleksiyonu klonlarken daha fazla belleğe ihtiyaç duyarlar.
  • Fail Safe yineleyicilerinin örnekleri ConcurrentHashMap, CopyOnWriteArrayList, vs.’dir.

--

--

No responses yet