Biraz Daha Aynı, Ama Yine Farklı: Java’da Karşılaştırmalara Devam

Didem AĞDOĞAN
5 min readDec 16, 2024

--

Part 2

Bir önceli yazımıza devam ediyoruz. Çok uzun bir yazı olmaması için 2 yazı olarak ele aldım.

Runnable ve Thread

Bir sınıfın Thread sınıfından miras alması, o sınıfın bir iş parçacığı (thread) olarak çalıştırılmasını sağlar. Yani, Thread sınıfından türeyen bir sınıf, aynı anda birden fazla işlem yürütmek için çoklu iş parçacığı (multithreading) yeteneği kazanır. Bu durum, Java’da paralel işlem yapmayı sağlar.

Java’da bir sınıfın Runnable arayüzünden (interface) implement edilmesi, o sınıfın bir “iş” (task) olarak tanımlanmasını sağlar. Ancak, bir sınıf Runnable arayüzünden miras aldığında, doğrudan bir iş parçacığı (thread) değil, bir iş parçacığında çalıştırılabilecek bir görev (task) olur. Runnable kullanımı, daha modüler ve esnek bir kod yapısı sağlar.

Peki Farkları Neler?

  • Thread sınıfını genişlettiğimizde, Java’nın çoklu kalıtımı desteklememesi nedeniyle başka bir sınıfı genişletemeyiz. Ancak, Runnable arayüzünü uyguladığımızda, gerekirse başka bir temel sınıfı da genişletebiliriz.
  • Thread sınıfını genişlettiğimizde, her iş parçacığı (thread) kendine özgü bir nesne oluşturur ve ona bağlanır. Buna karşılık, Runnable arayüzünü uyguladığımızda, birden fazla iş parçacığı aynı nesneyi paylaşabilir.
  • Thread sınıfı, getPriority(), isAlive() gibi birçok yerleşik metot sunarken, Runnable arayüzü yalnızca bir metot sağlar: run().

Thread sınıfı, iş parçacıklarını oluşturmak ve yönetmek için basit bir mekanizma sunuyor olsada, Java’nın tek kalıtım kısıtlaması nedeniyle sınırlamalara sahiptir. Öte yandan, Runnable, daha iyi bir nesne yönelimli tasarım, yeniden kullanılabilirlik ve kaynak paylaşımı sağlar. Bu nedenle, birçok senaryoda Runnable daha çok tercih edilen bir seçenektir.

Wait ve Sleep

Bu iki metot arasındaki temel fark, kilidin serbest bırakılıp bırakılmamasıdır. wait() eşzamanlı senkronizasyon için kullanılırken, sleep() genellikle bir iş parçacığını zamanlayıcıya dayalı olarak geçici olarak duraklatmak için kullanılır.

Peki Farkları Neler?

  • wait() metodu, Object sınıfında tanımlanmıştır.
  • wait() metodu çağrıldığında, iş parçacığı beklerken kilidi (lock) serbest bırakır. Bu, diğer iş parçacıklarının aynı monitörü kullanmasına izin verir.
  • sleep() metodu, Thread sınıfında tanımlanmıştır.
  • sleep() metodu çağrıldığında, iş parçacığı belirtilen süre boyunca uykuya geçer ancak kilidi (lock) serbest bırakmaz. Diğer iş parçacıkları aynı monitöre erişemez.

Runnable ve Callable

Callable ve Runnable; her ikisi de bir iş parçacığında (thread) işlerin gerçekleştirilmesi için kullanılır. Ancak, her birinin farklı avantajları ve kullanım senaryoları vardır.

Peki Farkları Neler?

  • Callable arayüzü, call() metodunu tanımlar ve bir sonuç döndürür ve kontrol edilen bir istisna (checked exception) fırlatabilir.
  • Runnable arayüzü sadece run() metodunu tanımlar. Kontrol edilen istisnaları (checked exceptions) doğrudan desteklemez. Eğer bir hata durumuyla karşılaşılırsa, bu istisnayı işlemek için try-catch blokları kullanılır.
  • Runnable bir değer döndürmez ve yalnızca işlemleri yürütmek için tasarlanmıştır.
  • Callable genellikle ExecutorService ile birlikte kullanılır ve sonuçlar Future nesnesi aracılığıyla alınır.

RunAsync ve SupplyAsync

Java’da CompletableFuture sınıfı, asenkron işlemleri yönetmek için kullanılan bir sınıftır. Bu sınıfta runAsync ve supplyAsync metotları, farklı senaryolarda kullanılır.

Peki Farkları Neler?

  • runAsync metodu, bir Runnable görevini kabul eder ve geriye herhangi bir değer döndürmez. Genellikle bir yan etki oluşturacak (örneğin, bir dosya yazma, veri tabanına kayıt ekleme) işlemleri asenkron olarak çalıştırmak için kullanılır.
  • supplyAsync metodu, bir Supplier görevini kabul eder ve geriye bir değer döndürür. Asenkron olarak bir hesaplama yapmak ve sonucunu geri döndürmek için kullanılır.
  • runAsync daha az kaynak gerektirir çünkü yalnızca bir iş parçacığı başlatır.
  • supplyAsync ise daha fazla kaynak kullanabilir çünkü iş parçacığı ve işin sonucu için bir Future nesnesi ile birlikte çalışır.
CompletableFuture.runAsync(() -> {
// Bu blok asenkron olarak çalışır
System.out.println("İşlem yapılıyor...");
});
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
// Bu blok asenkron olarak çalışır ve bir değer döndürür
return 42;
});

Arraylist ve Linkedlist

ArrayList, bir dizi kullanarak veri depolar. Bu, hızlı rastgele erişim sağlar. LinkedList, bir çiftli bağlantılı liste kullanır. Elemanlar bir dizi boyunca birbirine bağlantılı düğümler olarak saklanır.

Peki Farkları Neler?

  • ArrayList: Rastgele erişim ve okuma gerektiren durumlar için uygundur. Örneğin, veritabanından veri okumak.
  • ArrayList: Ekleme ve silme işlemleri zaman alır (O(n)), bu yüzden işlemlerin sıkça yapıldığı durumlarda uygun değildir.
  • ArrayList: Cache dostudur, çünkü elemanları bellekte ardışık olarak tutar ve bu da bellek erişim hızını artırır.
  • LinkedList: Düğümler arasında hızlı ekleme ve silme gerektiren durumlar için uygundur. Örneğin, kullanıcı arayüzü uygulamalarında düğüm eklemek veya silmek.
  • LinkedList: Herhangi bir noktada düğüm ekleme veya silme işlemleri hızlıdır (O(1)), bu yüzden bu tür işlemler için uygundur.
  • LinkedList: Cache dostu değildir çünkü her düğüm bağımsız bellekte depolanır ve bu da bellek erişim hızını azaltabilir.

ArrayList, veri okuma senaryoları için daha uygunken, LinkedList sıkça ekleme ve silme işlemleri yapılan senaryolar için daha uygundur.

Shallow Copy ve Deep Copy

Shallow Copy orijinal nesnenin yeni bir örneğini oluşturur, ancak nesnenin içerdiği objelerin kendilerini değil, yalnızca referanslarını kopyalar. Temel veri türleri (örneğin int, boolean, float) doğrudan kopyalanır. Nesne referansları kopyalanmaz, orijinal nesneyle aynı objelere işaret etmeye devam ederler.

Deep Copy orijinal nesnenin tam bir kopyasını oluşturur, orijinal nesnenin içerdiği tüm objeleri de kopyalar. Bu, hem temel verileri hem de objelere işaret eden referansları içerir ve nesnelerin kendilerini kopyalar.

Peki Farkları Neler?

  • Shallow Copy; nesne referanslarını paylaşır. Sadece referanslar kopyalandığı için daha hızlıdır.
  • Shallow Copy; orijinal nesne değiştirildiğinde, içerdiği nesneler değiştirildiğinde orijinal nesneyi etkileyebilir.
  • Deep Copy; tamamen yeni nesneler ve yeni referanslar oluşturur. Nesne hiyerarşisindeki tüm nesnelerin kendilerini kopyaladığı için daha karmaşıktır.
  • Deep Copy; orjinal nesnenin kopyası, bağımsız olduğu için, değişiklikler orijinal nesneyi etkilemez.

OpenJDK ve Oracle JDK

OpenJDK: OpenJDK (Open Java Development Kit), Java SE (Standard Edition) platformunun açık kaynaklı bir uygulamadır. Oracle topluluk tarafından geliştirilir. GPL v2 with Classpath Exception lisansı ile gelir. Bu, ticari projelerde de ücretsiz kullanım sağlar ve açık kaynak toplulukları için uygundur.

Avantajları:

  • Tamamen ücretsiz.
  • Java SE ile uyumluluk.
  • Topluluk tarafından sürekli geliştirilen ve test edilen bir proje.

Dezavantajları:

  • Ticari destek sunmaz (Oracle’dan ayrı olarak).
  • Güncellemeler bazen Oracle JDK’dan daha geç gelebilir.

Oracle JDK : Oracle JDK, Oracle’ın geliştirdiği, test ettiği ve optimize ettiği ticari bir JDK dağıtımıdır. 2019’dan itibaren Oracle JDK, Oracle Teknoloji Ağı Lisansı (OTN) kapsamında gelir. Bu lisans, ticari kullanımlar için ücretlidir, ancak geliştirme, test ve öğrenim amaçlı kullanımlar ücretsizdir.

Avantajları:

  • Oracle tarafından sağlanan optimize edilmiş performans.
  • Ticari uygulamalar için kapsamlı destek.
  • Uzun vadeli destek (Long-Term Support — LTS) sürümleri.

Dezavantajları:

  • Ticari kullanımlar için ücretlidir.
  • Açık kaynak topluluğu tarafından geliştirilen özelliklere erişim sınırlıdır.

Http1 ve Http2

HTTP (Hypertext Transfer Protocol), istemci (client) ile sunucu (server) arasındaki iletişimi sağlayan bir protokoldür. HTTP/1.1 ve HTTP/2, web performansını ve kullanıcı deneyimini optimize etmek için farklı tasarım yaklaşımları sunar.

HTTP/1.1

  • Her bir statik dosya (.css, .js, .png vs.) için ayrı bir HTTP isteği gönderilir. Bu, istemci-sunucu arasındaki bağlantı sayısını artırır ve yükleme süresini olumsuz etkiler.
  • HTTP/1.1, tüm mesajları metin tabanlı olarak iletir ve bu durum okunabilirliği sağlasa da daha fazla bant genişliği tüketir.
  • Her istekte, header bilgileri sıkıştırılmadan gönderilir. Bu durum, özellikle büyük veriye sahip başlıkların taşınması sırasında gecikmeye yol açabilir.
  • Bir HTTP isteğine yalnızca bir yanıt döner (1:1 model).

HTTP/1.1 Dezavantajları:

  • Her bir istekte yeni TCP bağlantısı kurulması gerekebilir (Keep-Alive kullanılmazsa).
  • Bir isteğin tamamlanmasını beklemek, diğer isteklerin yanıtlarını geciktirir.

HTTP/2

  • HTTP/2, HTTP/1.1'in sınırlamalarını gidermek ve web uygulamalarının performansını artırmak için tasarlanmıştır.
  • Tüm istekler, tek bir TCP bağlantısı üzerinden aynı anda gönderilebilir ve alınabilir. Bu, paralel veri iletimini sağlar ve bekleme sürelerini azaltır.
  • HTTP/2, metin yerine ikili (binary) format kullanır. Bu durum, protokolü daha hızlı ve daha verimli hale getirir. Veriler küçük parçalara (frame’ lere) ayrılarak taşınır.
  • HTTP/2, header bilgilerini sıkıştırarak gönderir. Bu, özellikle büyük başlıkların tekrar tekrar gönderilmesini engelleyerek performansı artırır.
  • İstemci tarafından talep edilmeden, sunucu önceden yüklenmesi gereken kaynakları gönderebilir. Örneğin, HTML belgesi ile birlikte CSS ve JS dosyalarının otomatik olarak gönderilmesi.
  • Bir isteğe birden fazla yanıt dönebilir (örneğin, farklı frame’ ler şeklinde).

HTTP/2 Avantajları:

  • Tek bir TCP bağlantısı üzerinden birden fazla istek ve yanıt gerçekleştirilir.
  • Multiplexing ve sıkıştırma teknikleri sayesinde yanıt süreleri daha hızlıdır.
  • İsteklerin ve yanıtların önceliği belirlenebilir.

Bir sonraki yazıda görüşmek üzere.

--

--

Didem AĞDOĞAN
Didem AĞDOĞAN

Written by Didem AĞDOĞAN

Software Developer, Traveller, Curious

No responses yet