Herhangi bir uygulamada birden fazla işlem olabilir (örnekler). Bu işlemin her biri, tek bir iş parçacığı veya birden çok iş parçacığı olarak atanabilir.
Bu yazıda aynı anda birden fazla görevi nasıl gerçekleştireceğimizi ve aynı zamanda bu görevler arasındaki senkronizasyon hakkında daha fazla bilgi öğreneceğiz.
Single Thread Nedir?
Single thread (tek iş parçacığı), bir bilgisayar programının veya işleminin aynı anda yalnızca bir işlemi gerçekleştirebildiği bir işleme modelini ifade eder. Bu model, bir programın sıralı olarak adım adım çalıştığı ve aynı anda birden fazla görevi yerine getiremediği anlamına gelir.
Programlamada, iki farklı tip thread kullanılır: User Thread (Kullanıcı İş Parçacığı) ve Daemon Thread (Arka Plan İş Parçacığı). Bu iki tür iş parçacığı, uygulamanın farklı ihtiyaçlarına yönelik olarak tasarlanmıştır.
User Thread
User Thread (Kullanıcı İş Parçacığı): Bu tip iş parçacığı, kullanıcı tarafından başlatılan ve kontrol edilen bir işlemler grubunu temsil eder. Kullanıcı iş parçacıkları, uygulama kodu tarafından oluşturulur ve kullanıcı tarafından yönetilir. Bu iş parçacıkları, genellikle kullanıcının talepleri doğrultusunda belirli görevleri gerçekleştirmek üzere kullanılır.
Daemon Thread
Daemon Thread (Arka Plan İş Parçacığı): Bu tür iş parçacığı, genellikle uygulamanın temizlenmesi (shutdown) sürecinde arka planda çalışan, kullanıcı tarafından doğrudan kontrol edilmeyen işlemleri yönetir. Örneğin, bir uygulamanın sonlanması sırasında kaynakları serbest bırakmak veya belirli işlemleri tamamlamak için kullanılabilirler. Daemon thread’leri genellikle programın ana işlevselliğini etkilemeden arka planda çalışan görevleri yönetmek için kullanılır.
Kullanım Örnekleri
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | public class ThreadExample { public static void main(String[] args) { // User Thread örneği Thread userThread = new Thread(() -> { System.out.println("User Thread Başlatıldı"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("User Thread Tamamlandı"); }); // Daemon Thread örneği Thread daemonThread = new Thread(() -> { System.out.println("Daemon Thread Başlatıldı"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Daemon Thread Tamamlandı"); }); daemonThread.setDaemon(true); // İş parçacıklarını başlatma userThread.start(); daemonThread.start(); try { // Ana programın beklemesi userThread.join(); daemonThread.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Ana Program Tamamlandı"); } } |
Yukarıdaki Java kodu, kullanıcı iş parçacığı (userThread
) ve daemon iş parçacığı (daemonThread
) örneklerini göstermektedir. Thread.sleep()
metodu kullanılarak her iki iş parçacığı da belli bir süre bekletilmektedir. daemonThread.setDaemon(true)
ifadesi ile daemon iş parçacığı olarak işaretlenmiştir. Bu sayede, kullanıcı iş parçacığı tamamlanmadan önce daemon iş parçacığı programın sonlanmasını beklemeden kendi işlemini tamamlayabilir.
Java’da Thread
sınıfının kullanımı oldukça yaygındır ve bu örnek, konseptin anlaşılması için temel bir yapı sağlamaktadır.
Single Thread modelinin avantajları şunlardır:
- Basitlik ve Anlaşılırlık: Single Thread modeli, sıralı bir şekilde işlemlerin gerçekleştirilmesine dayandığından, kodun basit ve anlaşılır olmasını sağlar. Her adım sırasıyla gerçekleşir, bu da kodun takip edilmesini kolaylaştırır.
- Düzenlilik ve Tahmin Edilebilirlik: Tek iş parçacığı kullanmak, işlemlerin düzenli bir şekilde tamamlanmasını sağlar. Bu düzenlilik, programın beklenen bir şekilde davranmasını ve hataların daha kolay tespit edilmesini sağlar.
- Synchronization Sorunları Yoktur: Birden fazla iş parçacığının eşzamanlı olarak çalıştığı çoklu iş parçacığı modellerindeki gibi senkronizasyon sorunlarından kaçınılır. Tek bir iş parçacığı olduğu için paylaşılan kaynaklara erişimle ilgili sorunlar daha az olabilir.
- Platform Bağımsızlık: Single Thread modeli, genellikle platform bağımsızdır. Programın çalışma şekli genellikle işletim sisteminden çok programın kendisine bağlıdır, bu da platform değişikliklerinin etkilerini azaltabilir.
- Hafif ve Düşük Kaynak Kullanımı: Tek iş parçacığı kullanan programlar, çoklu iş parçacığı kullananlara göre daha az bellek ve işlemci gücü tüketebilir. Bu durum, özellikle basit ve küçük ölçekli uygulamalarda avantajlı olabilir.
- Debugging Kolaylığı: Single Thread modeli, hata ayıklama sürecini basitleştirebilir. Çünkü her şey sıralı bir şekilde gerçekleştiği için, hataların kaynağını bulmak ve düzeltmek daha kolay olabilir.
Ancak, bu avantajların yanında Single Thread modelinin sınırlamalarını ve özellikle performans açısından bazı durumlarda yetersiz kalabileceğini unutmamak önemlidir. Çoklu iş parçacığı kullanmanın avantajlarına ve uygulamanın ihtiyaçlarına bağlı olarak, bu iki model arasında bir tercih yapmak gerekebilir.
Multithreading Nedir?
Java’da multithreading, aynı anda iki veya daha fazla iş parçacığının yürütme işlemidir. Bu, bilgisayarın CPU kaynaklarını daha etkili bir şekilde kullanarak programların daha hızlı ve verimli çalışmasını sağlar. Multithreading, özellikle büyük ve karmaşık uygulamalarda performans artışı elde etmek için yaygın olarak kullanılır.
Çalışma Prensibi
Her bir iş parçacığı (thread), kendi işlem akışına sahiptir ve bağımsız olarak çalışabilir. İş parçacıkları, aynı program içinde paylaşılan kaynaklara (örneğin, bellek) erişebilir, ancak bu durum senkronize edilmelidir. Multithreading, eşzamanlılık (concurrency) ilkesine dayanır; yani, birden fazla iş parçacığı aynı anda çalışabilir, ancak her biri kendi hızında ilerler.
Kullanım Örnekleri
Multithreading, çeşitli senaryolarda kullanılabilecek güçlü bir araçtır. İşte multithreading’in kullanım örnekleri ve detaylı açıklamaları:
1. İleri ve Geri Sayım Uygulaması:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | public class CountdownExample { public static void main(String[] args) { // İleri sayım iş parçacığı Thread countUpThread = new Thread(() -> { for (int i = 1; i <= 5; i++) { System.out.println("Count Up: " + i); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } }); // Geri sayım iş parçacığı Thread countDownThread = new Thread(() -> { for (int i = 5; i >= 1; i--) { System.out.println("Count Down: " + i); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } }); // İş parçacıklarını başlatma countUpThread.start(); countDownThread.start(); } } |
Bu örnekte, aynı anda iki iş parçacığı, biri ileri sayım yaparken diğeri geri sayım yapmaktadır.
2. Dosya İşleme Uygulaması:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | import java.io.*; public class FileProcessingExample { public static void main(String[] args) { // Dosya okuma iş parçacığı Thread readThread = new Thread(() -> { try { BufferedReader reader = new BufferedReader(new FileReader("input.txt")); String line; while ((line = reader.readLine()) != null) { System.out.println("Read: " + line); Thread.sleep(500); } } catch (IOException | InterruptedException e) { e.printStackTrace(); } }); // Dosya yazma iş parçacığı Thread writeThread = new Thread(() -> { try { BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt")); for (int i = 1; i <= 5; i++) { writer.write("Line " + i); writer.newLine(); System.out.println("Write: Line " + i); Thread.sleep(500); } writer.close(); } catch (IOException | InterruptedException e) { e.printStackTrace(); } }); // İş parçacıklarını başlatma readThread.start(); writeThread.start(); } } |
Bu örnekte, dosya okuma ve yazma işlemleri aynı anda iki iş parçacığı tarafından gerçekleştirilmektedir.
3. Web Scraping Uygulaması:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.select.Elements; import java.io.IOException; public class WebScrapingExample { public static void main(String[] args) { // Web scraping iş parçacığı Thread scrapingThread = new Thread(() -> { try { Document document = Jsoup.connect("https://www.example.com").get(); Elements links = document.select("a[href]"); links.forEach(link -> System.out.println("Link: " + link.attr("href"))); } catch (IOException e) { e.printStackTrace(); } }); // İş parçacığını başlatma scrapingThread.start(); } } |
Bu örnekte, web scraping işlemi için bir iş parçacığı kullanılmıştır. İnternet üzerinde veri çekme işlemleri aynı anda diğer işlemlerle paralel olarak gerçekleştirilebilir.
Bu örnekler, multithreading’in farklı senaryolarda nasıl kullanılabileceğini göstermektedir. Ancak, multithreading kullanırken senkronizasyon ve paylaşılan kaynaklara dikkat etmek önemlidir.
Avantajları
- Performans Artışı: Çoklu iş parçacığı kullanarak, programlar aynı anda birden fazla görevi gerçekleştirebilir, bu da toplam işlem hızını artırabilir.
- Daha Hızlı Cevap Süreleri: İş parçacıkları, bir işlem tamamlanmadan diğerine geçebilir, bu da kullanıcıya daha hızlı yanıt süreleri sunabilir.
- Paralel Programlama: Multithreading, görevleri paralel olarak işleyerek paralel programlamayı destekler.
- Daha Etkin Kaynak Kullanımı: İş parçacıkları, birbirinden bağımsız olarak çalıştıkları için kaynakların daha etkin bir şekilde kullanılmasını sağlar.
Dikkat Edilmesi Gerekenler
- Senkronizasyon Sorunları: Aynı anda çalışan iş parçacıkları arasında senkronizasyon sorunlarına dikkat edilmelidir. Paylaşılan kaynaklara aynı anda erişim, uyumsuzluklara yol açabilir.
- Deadlock ve Yarış Koşulu: Yanlış kullanıldığında, multithreading, deadlock (kilitlenme) ve yarış koşulu gibi sorunlara neden olabilir. Bu tür durumlar, dikkatli bir tasarım ve kodlama gerektirir.
- İşlemci Yoğunluğu: Çok sayıda iş parçacığı olması, işlemcinin yoğun olarak kullanılmasına ve sistem kaynaklarının tükenmesine neden olabilir.
Java’da iş parçacığı yaşam döngüsü
Java’da bir iş parçacığının yaşam döngüsü şu aşamalardan oluşur:
Aşama | Metot / Olay | Açıklama |
---|---|---|
Yaratma (Creation) | Thread Sınıfı İnstancesi | Bir iş parçacığı oluşturularak Thread sınıfının bir örneği yaratılır. |
Başlatma (Start) | start() Metodu | start metodu, iş parçacığı için yeni bir işlem başlatır ve run metodu çağrılır. |
Çalışma (Runnable) | run() Metodu | İş parçacığının ana çalışma mantığı bu metot içerisine yazılır. |
Bekleme (Sleep, Wait) | sleep() veya wait() Metodu | İş parçacığı, belirli bir süre boyunca bekletilebilir. sleep veya wait metotları kullanılır. |
Bekleme Sonrası Uyanma | – | Belirli bir süre boyunca bekleyen iş parçacığı, beklemenin sona ermesiyle uyanır ve çalışmaya devam eder. |
Bekleme Sonrası İnterupt | interrupt() Metodu | Bekleme sırasında bir iş parçacığına müdahale etmek ve onu uyandırmak için interrupt metodu kullanılabilir. |
Duraklatma (Suspend) | suspend() Metodu | İş parçacığı duraklatılır. Bu metot genellikle kullanılmaz, çünkü senkronizasyon sorunlarına neden olabilir. |
Devam Ettirme (Resume) | resume() Metodu | Duraklatılmış bir iş parçacığına devam etmesi için resume metodu kullanılabilir. Genellikle kullanılmaz. |
Durdurma (Stop) | stop() Metodu | İş parçacığı tamamen durdurulur. Bu metot genellikle kullanılmaz, çünkü güvenlik sorunlarına ve hatalara neden olabilir. |
Bitiş (Termination) | run Metodunun Tamamlanması | İş parçacığı, run metodunun tamamlanması veya stop metodu çağrıldığında sonlanır. |
1. Yaratma (Creation):
1 2 3 | Thread myThread = new Thread(new MyRunnable()); |
Yeni bir iş parçacığı oluşturulur. Thread
sınıfının bir örneği myThread
yaratılır. Bu aşamada iş parçacığı yaratılmış, ancak henüz başlatılmamış durumdadır.
2. Başlatma (Start):
1 2 3 | myThread.start(); |
start
metodu çağrılarak iş parçacığı başlatılır. Bu metodun çağrılması, iş parçacığının çalışma sürecini başlatır ve run
metodu otomatik olarak çağrılır.
3. Çalışma (Runnable):
1 2 3 4 5 6 7 8 | public class MyRunnable implements Runnable { public void run() { // İş parçacığının ana çalışma mantığı bu metot içerisine yazılır. System.out.println("İş parçacığı çalışıyor."); } } |
run
metodu içinde iş parçacığının ana çalışma mantığı yazılır. Bu kısımda işlemler gerçekleştirilir.
4. Bekleme (Sleep, Wait):
1 2 3 4 5 6 7 | try { Thread.sleep(1000); // 1000 milisaniye (1 saniye) bekletme } catch (InterruptedException e) { e.printStackTrace(); } |
İş parçacığı, belirli bir süre boyunca bekletilebilir. Bu örnekte, sleep
metodu kullanılarak iş parçacığı 1 saniye boyunca bekletilmektedir.
5. Bekleme Sonrası İnterrupt:
1 2 3 4 5 6 7 8 9 | try { synchronized (monitorObject) { monitorObject.wait(); // Diğer bir iş parçacığı tarafından uyanması için bekleme } } catch (InterruptedException e) { e.printStackTrace(); } |
Bekleme sırasında bir iş parçacığına müdahale etmek ve onu uyandırmak için interrupt
metodu kullanılabilir.
6. Duraklatma (Suspend):
1 2 3 | myThread.suspend(); |
İş parçacığı duraklatılır. Ancak, suspend
metodu güvenlik ve senkronizasyon sorunlarına neden olduğu için genellikle kullanılmaz.
7. Devam Ettirme (Resume):
1 2 3 | myThread.resume(); |
Duraklatılmış bir iş parçacığına devam etmesi için resume
metodu kullanılabilir. Ancak, bu metodun kullanımı güvenlik sorunlarına neden olabilir.
8. Durdurma (Stop):
1 2 3 | myThread.stop(); |
İş parçacığı tamamen durdurulur. Ancak, stop
metodu güvenlik sorunlarına ve hatalara neden olduğu için genellikle kullanılmaz.
9. Bitiş (Termination):
1 2 3 4 5 6 7 8 | public class MyRunnable implements Runnable { public void run() { // İş parçacığı çalıştıktan sonra sonlandırılır. System.out.println("İş parçacığı çalışmayı tamamladı."); } } |
run
metodu içindeki kodlar tamamlandıktan sonra iş parçacığı sonlandırılır. Bu durum, iş parçacığının çalıştığı görevin tamamlandığı durumu temsil eder.
java Thread Metotları
Threadler için yaygın olarak kullanılan yöntemlerden bazıları şunlardır:
Method | Tanımı | Örnek |
---|---|---|
start() | İş parçacığının yürütülmesini başlatır ve JVM iş parçacığı üzerinde run() yöntemini çağırır. | java Thread myThread = new MyThread(); myThread.start(); |
sleep(int millisecond) | İş parçacığının uyku moduna geçmesini sağlar, böylece iş parçacığı belirtilen milisaniye için duraklar ve ardından çalışmaya devam eder. Bu konuların senkronizasyonunda yardımcı olur. | java try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } |
getName() | Thread ismini döndürür. | java String threadName = myThread.getName(); |
setPriority(int newPriority) | Thread önceliğini değiştirir. | java myThread.setPriority(Thread.MAX_PRIORITY); |
yield() | Durmadaki mevcut thread ve diğer threadin yürütülmesine neden olur. | java Thread.yield(); |
Tablo üzerindeki metotlara örneklerle bakalım:
Bu örnekler, Java’da yaygın olarak kullanılan thread yöntemlerini göstermektedir. Ancak, setPriority
ve yield
gibi yöntemleri kullanırken dikkatli olunmalıdır, çünkü bazı durumlarda istenmeyen davranışlara neden olabilirler.
1. start()
1 2 3 4 5 6 7 8 9 10 11 12 | public class MyThread extends Thread { public void run() { System.out.println("Thread çalışıyor."); } public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.start(); } } |
Bu örnekte, start
metodu çağrılarak iş parçacığı başlatılır ve run
metodu otomatik olarak yürütülür.
2. sleep(int millisecond)
1 2 3 4 5 6 7 8 9 10 11 12 13 | public class SleepExample { public static void main(String[] args) { System.out.println("İş parçacığı başlıyor."); try { Thread.sleep(2000); // 2 saniye bekletme } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("İş parçacığı devam ediyor."); } } |
Bu örnekte, sleep
metodu kullanılarak iş parçacığı 2 saniye boyunca bekletilir.
3. getName()
1 2 3 4 5 6 7 8 9 | public class GetNameExample { public static void main(String[] args) { Thread myThread = new Thread(); String threadName = myThread.getName(); System.out.println("Thread ismi: " + threadName); } } |
Bu örnekte, getName
metodu kullanılarak iş parçacığının adı alınır.
4. setPriority(int newPriority)
1 2 3 4 5 6 7 8 | public class SetPriorityExample { public static void main(String[] args) { Thread myThread = new Thread(); myThread.setPriority(Thread.MAX_PRIORITY); // En yüksek öncelik } } |
Bu örnekte, setPriority
metodu kullanılarak iş parçacığının önceliği en yükseğe (MAX_PRIORITY) ayarlanır.
5. yield()
1 2 3 4 5 6 7 | public class YieldExample { public static void main(String[] args) { Thread.yield(); // Diğer threadlere kontrolü geçirme } } |
Bu örnekte, yield
metodu kullanılarak iş parçacığının kontrolü diğer threadlere geçirilir.
Bu örnekler, ilgili thread metotlarının nasıl kullanılacağını göstermektedir.
Java Thread Senkronizasyonu
Multithreading ve Senkronizasyon
Multithreading’de, programların zaman uyumsuz davranışı olabilir, özellikle de birden çok iş parçacığı aynı kaynaklara erişiyorsa. Bu durum, uygulamada tutarsızlıklara ve hatalara neden olabilir. Paylaşılan kaynaklara güvenli bir şekilde erişmek ve tutarlılık sağlamak için senkronizasyon kullanılır.
Java’da, senkronize davranış sağlamak için senkronize metotlar kullanılır. Senkronize bloklar, bir iş parçacığının belirli bir nesne üzerinde senkronize edilmiş bir bloğa ulaştığında, başka hiçbir iş parçacığının o bloğu aynı anda çağıramayacağı şekilde çalışır. Bu, bir iş parçacığının bir senkronize bloğu bitirmesini bekleyen diğer tüm iş parçacıklarına izin verir.
Örnek olarak:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | public class SynchronizationExample { private static final Object lockObject = new Object(); private static int sharedData = 0; public static void main(String[] args) { Runnable incrementTask = () -> { synchronized (lockObject) { // Senkronize blok içindeki ifadeler sharedData++; } }; Thread thread1 = new Thread(incrementTask); Thread thread2 = new Thread(incrementTask); thread1.start(); thread2.start(); try { thread1.join(); thread2.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Paylaşılan Veri: " + sharedData); } } |
Bu örnekte, synchronized
anahtar kelimesi kullanılarak lockObject
üzerinde senkronize bir blok oluşturulmuştur. Bu blok içindeki ifadeleri bir iş parçacığı yürütürken diğer iş parçacıkları beklemek zorundadır.
Bu senkronizasyon yöntemi, çoklu iş parçacıklı uygulamalarda veri tutarlılığını sağlamak için kullanılır ve paylaşılan kaynaklara güvenli erişimi temin eder.
Daha basit bir örnek ile devam edelim:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | public class SynchronizationSimpleExample { private static int sharedCounter = 0; public static void main(String[] args) { // Runnable arayüzünü kullanarak artırma işlemlerini tanımlayan lambda ifadesi Runnable incrementTask = () -> { // synchronized blok içindeki ifadeleri yalnızca bir iş parçacığı aynı anda çalıştırabilir synchronized (SynchronizationSimpleExample.class) { // Paylaşılan değişkeni artırmak için döngü for (int i = 0; i < 1000; i++) { sharedCounter++; } } }; // İki farklı iş parçacığını oluşturuyoruz Thread thread1 = new Thread(incrementTask); Thread thread2 = new Thread(incrementTask); // İş parçacıklarını başlatıyoruz thread1.start(); thread2.start(); try { // Ana iş parçacığı, diğer iş parçacıklarının tamamlanmasını bekliyor thread1.join(); thread2.join(); } catch (InterruptedException e) { e.printStackTrace(); } // Sonuçları ekrana yazdırma System.out.println("Paylaşılan Counter: " + sharedCounter); } } |
Açıklamalar:
sharedCounter
: Bu değişken, iki iş parçacığı tarafından paylaşılacak ve senkronize edilecek olan ortak bir sayacı temsil eder.Runnable incrementTask
: Bu,Runnable
arayüzünü kullanarak bir artırma görevini tanımlayan bir lambda ifadesidir. İki iş parçacığı aynıincrementTask
‘ı kullanacak vesharedCounter
‘ı artırmaya çalışacak.synchronized (SynchronizationSimpleExample.class)
:synchronized
anahtar kelimesi, parantez içine yazılan nesne üzerinde senkronizasyonu sağlar. Burada, sınıfın kendisi üzerinde senkronizasyon sağlıyoruz.for (int i = 0; i < 1000; i++)
: İki iş parçacığı, her biri 1000 defa dönen bir döngü içindesharedCounter
‘ı artırmaya çalışacak.Thread thread1 = new Thread(incrementTask);
: İki farklı iş parçacığı oluşturuyoruz, ancak her ikisi de aynıincrementTask
‘ı kullanacak.thread1.start();
vethread2.start();
: İki iş parçacığını başlatıyoruz. İş parçacıkları aynı anda çalışacak vesharedCounter
‘ı artırmaya çalışacaklar.thread1.join();
vethread2.join();
: Ana iş parçacığı, diğer iki iş parçacığının tamamlanmasını bekler. Bu, iş parçacıklarının tamamlanmasını beklerken diğer işlemlerin devam etmesini engeller.System.out.println("Paylaşılan Counter: " + sharedCounter);
: Sonuçları ekrana yazdırma. Senkronizasyon kullanıldığı için, iki iş parçacığı dasharedCounter
‘ı güvenli bir şekilde artırmış olacaktır.
Örnek 2:
Bu örnekte, çalıştırılabilir bir arabirimin run() ve start() yöntemini geçersiz kılma yöntemlerini ve bu sınıfın iki iş parçacığını oluşturup bunları uygun şekilde çalıştıracağız.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | package tasarimkodlama; public class TasarimKodlama { public static void main(String[] args) { TasarimThread thread1 = new TasarimThread("Tasarim1"); thread1.start(); TasarimThread thread2 = new TasarimThread("Tasarim2"); thread2.start(); } } class TasarimThread implements Runnable { Thread tasarimThread; private String tasarimAd; TasarimThread(String ad) { tasarimAd = ad; } @Override public void run() { System.out.println("Thread çalışıyor" + tasarimAd); for (int i = 0; i < 3; i++) { System.out.println(i); System.out.println(tasarimAd); try { Thread.sleep(1000); } catch (InterruptedException e) { System.out.println("Thread kesildi!"); } } } public void start() { System.out.println("Thread başladı!"); if (tasarimThread == null) { tasarimThread = new Thread(this, tasarimAd); tasarimThread.start(); } } } |
Başka bir örnek daha:
Eksik olan senkronizasyonu eklemek için synchronized
anahtar kelimesini kullanabiliriz. Aşağıda, run
metodu içindeki işlemleri senkronize etmiş bir şekilde güncellenmiş bir örnek bulunmaktadır:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | package tasarimkodlama; public class TasarimKodlama { public static void main(String[] args) { TasarimThread thread1 = new TasarimThread("Tasarim1"); thread1.start(); TasarimThread thread2 = new TasarimThread("Tasarim2"); thread2.start(); } } class TasarimThread implements Runnable { private Thread tasarimThread; private final String tasarimAd; private static final Object lock = new Object(); // Senkronizasyon için kullanılacak kilitleme nesnesi TasarimThread(String ad) { tasarimAd = ad; } @Override public void run() { synchronized (lock) { System.out.println("Thread çalışıyor " + tasarimAd); for (int i = 0; i < 3; i++) { System.out.println(i); System.out.println(tasarimAd); try { Thread.sleep(1000); } catch (InterruptedException e) { System.out.println("Thread kesildi!"); } } } } public void start() { System.out.println("Thread başladı!"); if (tasarimThread == null) { tasarimThread = new Thread(this, tasarimAd); tasarimThread.start(); } } } |
Bu örnekte, run
metodu içindeki işlemleri synchronized (lock)
bloğu içine aldık. Bu sayede TasarimThread
‘lere ait iki farklı iş parçacığı aynı anda bu bloğa giremez, bu da senkronizasyonu sağlar.
Multithreading ve Senkronizasyon Özeti
Java’da multithreading, birçok iş parçacığının aynı anda çalıştığı ve genellikle paylaşılan kaynaklara erişimde senkronizasyon sorunlarının ortaya çıkabileceği bir konsepttir. Senkronizasyon, bu tür sorunları önlemek ve tutarlılık sağlamak için kullanılır.
Senkronizasyon Yöntemleri:
- Synchronized Bloklar:
synchronized
anahtar kelimesi kullanılarak belirli bir nesne üzerinde senkronize bloklar oluşturulur.- Bu bloklar, aynı anda sadece bir iş parçacığı tarafından çalıştırılabilir, bu da veri tutarlılığını sağlar.
1 2 3 4 5 | synchronized (lockObject) { // Senkronize edilecek ifadeler } |
Lock ve ReentrantLock:
Lock
arayüzü veReentrantLock
sınıfı,synchronized
anahtar kelimesinden daha gelişmiş kontrol sağlar.lock()
veunlock()
metotları ile senkronizasyon sağlanır.
1 2 3 4 5 6 7 8 9 10 | Lock lock = new ReentrantLock(); lock.lock(); try { // Senkronize edilecek ifadeler } finally { lock.unlock(); } |
Örnek Uygulama:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | public class SynchronizationExample { private static final Object lockObject = new Object(); private static int sharedData = 0; public static void main(String[] args) { Runnable incrementTask = () -> { synchronized (lockObject) { sharedData++; } }; Thread thread1 = new Thread(incrementTask); Thread thread2 = new Thread(incrementTask); thread1.start(); thread2.start(); try { thread1.join(); thread2.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Paylaşılan Veri: " + sharedData); } } |
Bu örnek, senkronizasyonu kullanarak iki iş parçacığının aynı anda paylaşılan bir değişkene güvenli bir şekilde erişimini kontrol etmektedir. Senkronizasyon, çoklu iş parçacıklı uygulamalarda veri tutarlılığını sağlamak için önemli bir araçtır.
Yorum Yap