• Anasayfa
  • Eğitimler
    • JavaScript Eğitimi
    • Angular 2 Eğitimi
    • React.js Eğitimi
    • Java 8 Eğitimi
    • Java EE 7 Eğitimi
    • Spring Framework Eğitimi
    • Git Eğitimi
  • Online Eğitimler
    • Online React.js Eğitimi
    • Online Angular 2 Eğitimi
    • Online Spring Boot Eğitimi
  • Referanslar
  • Hakkında
  • İletişim
KodEdu
  • Anasayfa
  • Eğitimler
    • JavaScript Eğitimi
    • Angular 2 Eğitimi
    • React.js Eğitimi
    • Java 8 Eğitimi
    • Java EE 7 Eğitimi
    • Spring Framework Eğitimi
    • Git Eğitimi
  • Online Eğitimler
    • Online React.js Eğitimi
    • Online Angular 2 Eğitimi
    • Online Spring Boot Eğitimi
  • Referanslar
  • Hakkında
  • İletişim

Java 8 LongAdder vs AtomicLong

  • Posted by Kodedu
  • Categories backend, Genel, Uncategorized, Verimlilik, Yazılar
  • Date 23 Ocak 2015

Java 8 ile birlikte pek çok görünen veya arkada görünmeyen yenilikler getirildi. Bunlardan bir tanesi de LongAdder sınıfıdır. Bir benzeri olan AtomicLong sınıfı ise Java 5’ten beri var.

Her iki sınıf da hemen hemen aynı işi yapıyor. Yaptıkları iş ise, Long türündeki bir tamsayıya +1 eklemek veya -1 eklemek. Peki neden bu sınıflara ihtiyaç duyuluyor?

Bunu şu şekilde açıklayabiliriz. Elimizde aşağıdaki gibi Counter adında bir sınıfımız olsun. Bu sınıfın increment() and decrement() adında da iki metodu bulunsun. Bu metodların yapacağı iş ise bir tamsayıya bir eklemek ve çıkarmak olsun. Şöyle ki;

Counter sınıfı
public class Counter { (1)

    private Long count = 0L; (2)

    public void increment(){ (3)
        count++;
    }

    public void decrement(){ (4)
        count--;
    }

    public Long getCount(){ (5)
        return count;
    }

}
1 Sayaç sınıfı
2 count değişkeninin başlangıcı 0
3 count değeri 1 artırılıyor.
4 count değeri 1 azaltılıyor.
5 count ‘un o anki değeri döndürülüyor.

Burada herşey iyi hoş fakat, bu yapı thread-safe değil!! Aslında tek bir eylemmiş gibi görünen count++ ve count-- aslında birçok işten oluşuyor.

Şöyle ki;

count değerini oku (1)
count değerini bir artır veya azalt (2)
count değerini güncelle (3)

Bu üç iş 1-2 ve 2-3 ara noktalarında context-switch yapılabilmesine imkan verdiği için beklendik sonuç ile çıkan sonuç uyumsuz olabilecektir.

Örneğin 2 iş parçacığı (thread) aynı anda increment() metodunu çağırıyor olsun ve adımlar aşağıdaki gibi işlesin;

Thread 1 -> count değerini okur (1)
    Context-Switch gerçekleşir (2)
Thread 2 -> count değerini okur (3)
Thread 2 -> count değerini artırır (4)
Thread 2 -> count değerini günceller (5)
    Context-Switch gerçekleşir (6)
Thread 1 -> Okuduğu count değerini  bir artırır (7)
Thread 1 -> Artırdığı değeri günceller (8)
1 Thread 1 için count değeri 0 okunuyor
2 Diğer Thread’e geçiş
3 Thread 2 için count değeri 0 okunuyor
4 Thread 2 için count değeri 1 artırılıyor
5 Thread 2 için count değeri 1 güncelleniyor
6 Diğer Thread’e geçiş
7 Thread 1 için count değeri 1 artırılıyor
8 Thread 1 için count değeri 1 güncelleniyor

Bu senaryo sonunda count değişkeninin son değeri 1 olur. Fakat sonuç 2 olmalıydı. İşte bu sebeple bu yapı thread-safe değildir diyoruz.

Bu aslında atomiklik problemidir. Yani tek bir bütün gibi görünen işlem aslında birden fazla ayrık iştir. Bu sebeple context-switch ‘e imkan vererek beklenmedik sonuçlara sebep olmaktadır.

Bu üç işi tek bir iş yapabilmek için, bu işleri parçalanamaz (context-switch ile bölünemez) hale getirebiliriz (atom haline getirebiliriz). Birden fazla işi atomik hale getirmek için synchronized anahtar kelimesini veya ReentrantLock gibi nesnelerden faydalanabiliriz.

Örneğin;

Counter sınıfı
public class Counter {

    private Long count = 0L;

    public synchronized void increment(){ // synchronized
        count++;
    }

    public synchronized void decrement(){ // synchronized
        count--;
    }

    public synchronized Long getCount(){
        return count;
    }

}

Metodlar synchronized yapıldığında, T anında yalnızca 1 iş parçacığı increment ve decrement metodları içinde iş yapacaktır. Diğer iş parçacıkları ise, iş yapan iş parçacığın metodu terk etmesini bekleyeceklerdir.

Bu durum sonucunda thread-safety problemi çözülmüş olur, fakat yeni bir problem ortaya çıkar o da Performans problemi.

Yukarıdaki increment ve decrement metodları iş parçacıklarını hizaya koyar, aynı anda iş yapamaz hale gelirler. Bu da sayaç artırma/azaltma işlemi için darboğaz oluşturacağı anlamını taşıyor.

Sayaç artırma senaryosunda thread-safety ve performans problemlerini söndürmek için Java içerisinde AtomicInteger, AtomicLong ve LongAdder nesneleri bulunuyor. Atomic* olanlar Java 5’ten beri varken, LongAdder Java 8 ile henüz yeni getirildi. Fakat IntAdder diye bir sınıf getirilmediğini belirtmek isterim, sadece Long hali bulunuyor.

Atomic* sınıfları türünden nesneler, hem thread-safe ‘tir. Hem de yapılan işlemi yüksek başarımlı olarak gerçekleştirirler. LongAdder nesneleri ise, AtomicLong nesnesine göre kat kat daha fazla yüksek başarımla sayaç işlemlerini gerçekleştirirler. Fakat LongAdder nesnesi AtomicLong ‘a göre daha fazla bellek alanı tüketmektedir.

Yukarıda bahsettiklerimizi kanıtlamak için, sizlerle JMH (Java MicroBenchmark Harness) ile yaptığım kıyaslamayı paylaşmak isterim.

Counter vs AtomicLong vs LongAdder

JMH ile yapılan kıyaslamada, aşağıdaki niteliklere sahip Amazon EC2 makinası kullanılmıştır.

Example 1. Benchmark’da kullanılan sistem

Amazon EC2 – Compute Optimized (c3.8xlarge)
OS – Ubuntu 14.04 (x64)
CPU – 32 Core
Memory – 60 Gib
Disk – 2 x 320 SSD
Java – JDK 1.8 x64

JMH ile yapılan bu ölçüm sonucunda aşağıdaki çıktılar elde edilmiştir.

Thread sayısı Counter (opts/ms) AtomicLong (opts/ms) LongAdder (opts/ms)

1

41672.68909613381

140253.50851349483

88159.29678717554

2

21427.389750899158

80461.53894458264

176168.92737348034

3

28647.729284927846

76716.30113465142

261230.59792909838

4

27209.234760929074

67990.24783775007

351318.7946836924

5

27465.74504786415

69041.89169888754

439091.1919015038

6

25838.56806379749

66226.24153866756

527889.262511702

7

27895.005716758176

70962.58786769061

604720.7845669618

8

29824.63604508381

72030.62263077003

700302.8153259079

9

23160.47999126532

71234.49684954488

778763.7886768953

10

29224.18154845496

69897.36028303344

861418.8185465168

11

16948.03390480977

69974.02050098566

941304.4224780009

12

26730.29944976108

69021.07450449596

1016534.1595596515

13

16025.220262360124

73130.64286118638

1085067.0573038626

14

19862.662553970724

71855.46330526042

1150916.1856743009

15

21864.71243784617

66756.24362408795

1206257.065200144

16

14311.927044503234

70172.81000368376

1286617.7092898681

17

13812.790859316134

72771.08592896884

1364026.7288176445

18

12915.302399663751

68714.59365814141

1407000.6514096512

19

14079.797370133612

67909.2908569055

1448556.4113242174

20

13781.660590830888

70911.64805900287

1525443.4720664842

21

13661.317043400575

67420.32688299088

1590701.745499809

22

13123.256421277907

74014.52396619573

1512402.6709205445

23

14046.940134185787

74219.95136660068

1668385.0168750533

24

13883.126332713113

66822.51696197722

1704745.2462318498

25

13695.700287741183

67392.98538139547

1771630.2211731498

26

14263.609176633352

72078.81502528508

1803194.4472211923

27

14235.15711567844

69513.41638494289

1827386.1809486842

28

12897.078507902097

65653.42458349481

1881694.8392604052

29

14684.357169859464

70201.21669138117

1930174.7003092975

30

14566.82924130262

71106.7300332613

1726288.8632039533

31

14442.104127009969

70359.76043375299

2016763.8781386625

32

14664.16730602341

72063.45431988215

2000693.716820047

opts/ms : 1 milisaniyede yapılan operasyon sayısı. (Throughput)

Yukarıdaki tablonun grafiksel görünümü ise aşağıdaki gibidir. Sonuç gerçekten dramatik.

atomiclong-vs-longadder

Mavi: LongAdder
Kırmızı: AtomicLong
Siyah: Klasik sayaç

Benchmark uygulamasına https://github.com/rahmanusta/jmh-samples bağlantısından erişebilirsiniz.

Tekrar görüşmek dileğiyle..

Tag:atomiclong, backend, jmh, longadder

  • Share:
author avatar
Kodedu

Previous post

Mininet ile Yazılım Tanımlı Ağlar (Software Defined Networks)
23 Ocak 2015

Next post

First Look at JSR 371, MVC 1.0 Spesification and Ozark RI
20 Şubat 2015

You may also like

api-logo
Swagger Nedir? Neden kullanılır?
10 Ekim, 2018
spring-cli-logo
Spring CLI ile Spring Boot Projeleri Hazırlamak
21 Ağustos, 2017
eureka_architecture
Spring Cloud Netflix ve Eureka Service Discovery
3 Temmuz, 2017

    2 Comments

  1. mkayman
    18 Mart 2015
    Cevapla

    LongAdder bu kadar performans kazancını nasıl sağlamış merak eden olursa buraya bakabilir. https://minddotout.wordpress.com/2013/05/11/java-8-concurrency-longadder/

    Yazı çok güzeldi, teşekkürler.

    • Rahman Usta
      18 Mart 2015
      Cevapla

      Teşekkürler. Güzel paylaşımınız için ben de teşekkür ederim.

Leave A Reply Cevabı iptal et

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

E-posta listesine kayıt olun!






Gözde yazılar

Java Mimarisiyle Kurumsal Çözümler : Kurumsal Java Kitabı
16Eyl2012
Java API for JSON Processing – Stream bazlı JSON Üretmek ve Tüketmek
06Ağu2013
AsciidocFX projesi Duke’s Choice Award 2015 ödülünü kazandı
28Eki2015
Knockout.js – Hesap Makinesi Örneği
02Şub2013

Son Yazılar

  • Java’da Record’lar 27 Ocak 2020
  • Swagger Nedir? Neden kullanılır? 10 Ekim 2018
  • Spring CLI ile Spring Boot Projeleri Hazırlamak 21 Ağustos 2017
  • Spring Cloud Netflix ve Eureka Service Discovery 3 Temmuz 2017
  • Online React.js Eğitimi ardından (15-25 Mayıs 2017) 31 Mayıs 2017

Son Yorumlar

  • Coupling ve Cohesion Kavramları Nedir? için Hilal
  • Naïve Bayes Sınıflandırma Algoritması için Rahman Usta
  • Naïve Bayes Sınıflandırma Algoritması için Mete
  • YAML Nedir? Neden YAML Kullanmalıyız? için kara
  • JWT (JSON Web Tokens) Nedir? Ne işe yarar? için Furkan

Get Java Software

Arşivler

Bizi takip edin

React.js Eğitimi Başlıyor
11-22 Eylül, 2017
Eğitmen
Rahman Usta
İletişim

merhaba@kodedu.com

  • Hakkında
  • Gizlilik Politikası
  • İletişim
  • Referanslar
Kodedu Bilişim Danışmanlık
Cemil Meriç mah. Çelebi sok.
No:16/3 Ümraniye/İSTANBUL
Tel: 0850 885 38 65
Alemdağ V.D.: 8960484815

KODEDU © Tüm hakları saklıdır.