3 Mayıs 2023 Çarşamba

ConcurrentHashMap. compute metodu - Atomic Metod

Giriş
Atomic çalışır. key değerinin var olup olmadığın bakmaksızın lambda'yı çalıştırır. Key yoksa yeni bir value nesne ekleme, varsa da value nesnesini güncelleme imkanı sağlar.

merge İçin Kullanma
ConcurrentHashMap merge() metodu da sağlıyor. Aradaki fark şu. 
1. compute() Açısından
- compute() metoduna geçilen BiFunction her zaman çağrılır. BiFunction'a geçilen value null olabilir. 
BiFunction  sadece key varken çağrılsın istiyorsak computeIfPresent() metodunu kullanmak gerekir.

merge() Açısından
- merge() metoduna geçilen ikinci parametre key yoksa kullanılacak varsayılan değer. Yani default value
- BiFunction sadece mevcut değer null değilse çağrılır ve eski ve yeni değeri görebilir, key değerini göremez
- Yani merge() daha özel bir metod ve sadece yeni değeri hesaplamak için kullanılır
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("one", 1);

// existing value is 1, so new value is 11
map.merge("one", 10, Integer::sum);
// key "two" does not exist, so value is 2
map.merge("two", 2, Integer::sum);
Açıklaması şöyle
Differences between compute() and merge() are subtle but important. With the former, the BiFunction will be called in any case, while with the latter, it will only if the value is not null. Also, note that the BiFunction arguments refers to different arguments depending on the method.

compute ve computeIfAbsent Farkı
Örnek
Elimizde şöyle bir kod olsun. Bu kod yanlış. Çünkü ilklendirilen nesne thread safe olarak güncellenmiyor.
valueKeyMap.computeIfAbsent(value, val -> new HashSet<>()).add(key);
Şöyle yapmak daha gerekir.
valueKeyMap.compute(value, (k, v) -> {
  if (v == null) {
    v = new HashSet<>();
  }
  v.add(key);
  return v;
});
compute metodu - key + BiFunction
İki thread'in sırayla bir value değerini güncelleyebilmesini sağlar. Thread safe çalışır, 
1. key yoksa yeni bir value yaratır
1. key varsa ve value null ise yeni bir value yaratır
3. key varsa ve  value null değilse value nesnesini değiştirme imkanı sağlar. Açıklaması şöyle.
Since Java 8 we can use .compute* methods on ConcurrentHashMap to synchronize processing by key, so that if two threads execute .compute* method on the same key at the same time, callbacks still will be executed one after another and not simultaneously.
Açıklaması şöyle.
Attempts to compute a mapping for the specified key and its current mapped value (or null if there is no current mapping). The entire method invocation is performed atomically. Some attempted update operations on this map by other threads may be blocked while computation is in progress, so the computation should be short and simple, and must not attempt to update any other mappings of this Map.
İmzası şöyle.
public V compute(K key,
                 BiFunction<? super K, ? super V, ? extends V> remappingFunction)
Örnek - BiFunction İçinde Exception
Açıklaması şöyle
It's important to note that if the BiFunction provided to Map.compute throws an exception, the value associated with the key in the map will not be updated. The map will remain unchanged, and the exception will be propagated to the calling code.
Şöyle yaparız
Map<String, Integer> map = new HashMap<>();
map.put("one", 1);

// BiFunction that throws an exception
try {
  map.compute("one", (key, value) -> {
    throw new RuntimeException("Error occurred while computing new value");
  });
} catch (Exception e) {
  // handle the exception
  System.out.println("Exception occurred: " + e.getMessage());
}

Örnek - Ekleme Veya Güncelleme

Şöyle yaparız.. Burada 
1. key olarak A değeri hiç yoksa value zaten null olacağı için yeni bir value yaratır
2.key olarak A değeri varsa ancak value null ise yeni bir value yaratır
2.key olarak A değeri varsa ve value null değilse value nesnesini günceller
map.compute("A", (key, value) -> {
  if (value == null) {
    return new POJO();
  }
  value.a = ++value.a;
  value.b = ++value.b;

  return value;
});

Hiç yorum yok:

Yorum Gönder