multithreading etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
multithreading etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

Java'da Multithreading ve Concurrency Soruları ve Cevapları - Martin Mois Çevirisi - Devam edecek...


Yazının orjinaline buradan ulaşabilirsiniz.

Eşzamanlılık (Concurrency) teriminden ne anlıyoruz?

Concurrency, bir programın birkaç hesaplamayı aynı anda yürütme yeteneğidir. Bu, hesaplamaları bir makinenin kullanılabilir CPU çekirdeklerine veya hatta aynı ağ içindeki farklı makinelere dağıtarak elde edilebilir.

İşlemler(Process) ve iş parçacıkları(Threads) arasındaki fark nedir?

İşlemler, işletim sistemi tarafından sağlanan ve kendi özel kaynaklarına (ör. bellek, açık dosyalar vb.) sahip bir yürütme ortamıdır. İş parçacıkları, süreçlerin aksine, bir işlem içinde yaşar ve kaynaklarını (bellek, açık dosyalar vb.) işlemin diğer İş parçacıkları ile paylaşır. Kaynakları farklı İş parçacıkları arasında paylaşma yeteneği, İş parçacıkları performansın önemli bir gereksinim olduğu tasklar için daha uygun hale getirir.

Java'da, process ve thread nedir?

Java'da İşlemler (process), çalışan bir Java Sanal Makinesi'ne (JVM) karşılık gelirken, İş parçacıkları (threads) JVM içinde yaşar ve çalışma zamanında Java uygulaması tarafından dinamik olarak oluşturulabilir ve durdurulabilir.

Zamanlayıcı (Scheduler) nedir?

Zamanlayıcı, işlemlerin ve iş parçacıklarının işlemci veya bazı G/Ç kanalları gibi bazı sınırlı kaynaklara erişimini yöneten bir zamanlama algoritmasının uygulanmasıdır. Çoğu zamanlama algoritmasının amacı, her işlemin/thread'ın özel olarak istenen kaynağa erişmek için uygun bir zaman çerçevesi almasını garanti eden mevcut işlemler/iş parçacıkları için bir tür yük dengeleme sağlamaktır.

Bir Java programında en az kaç thread vardır?

Her Java programı ana(main) thread içinde yürütülür; dolayısıyla her Java uygulamasının en az bir thread vardır.

Bir Java uygulaması mevcut thread'a nasıl erişebilir?

Geçerlit hread'a, JDK sınıfı java.lang.Thread'in currentThread() statik yöntemi çağrılarak erişilebilir:



Her Java thread'ın hangi özellikleri vardır?

Her Java thread aşağıdaki özelliklere sahiptir:
• JVM içinde benzersiz olan long türünde bir tanımlayıcı (identifier)
• String türünde bir ad (name)
• int türünde bir öncelik(priority)
• java.lang.Thread.State türünde bir durum (state)
• thread'ın ait olduğu bir thread grubu (thread group)

Thread gruplarının amacı nedir?

Her thread bir thread grubuna aittir. JDK sınıfı java.lang.ThreadGroup, tüm thread grubunu işlemek için bazı yöntemler sağlar. Bu yöntemlerle, örneğin bir grubun tüm iş parçacıklarını kesebilir veya maksimum önceliklerini ayarlayabiliriz.

Bir thread'ın hangi durumları olabilir ve her bir durumun anlamı nedir?

YENİ (NEW): Henüz başlamamış bir thread bu durumdadır.

ÇALIŞTIRILABİLİR(RUNNABLE): Java sanal makinesinde çalışan bir thread bu durumdadır.

BLOCKED: Bir monitör kilidi beklerken bloke edilmiş bir thread bu durumdadır.

BEKLEMEKTE(WAITING): Başka bir thread'ın belirli bir eylemi gerçekleştirmesini süresiz olarak bekleyen bir thread bu durumdadır.

TIMED_WAITING: Belirli bir bekleme süresi kadar başka bir thread'ın bir eylem gerçekleştirmesini bekleyen bir thread bu durumdadır.

SONLANDIRILDI(TERMINATED): Çıkmış olan bir thread bu durumdadır.

Bir thread'ın önceliğini nasıl belirleriz?

Bir thread'ın önceliği, setPriority(int) yöntemi kullanılarak belirlenir. Önceliği maksimum değere ayarlamak için Thread.MAX_PRIORITY sabitini ve minimum değere ayarlamak için Thread.MIN_PRIORITY sabitini kullanırız, çünkü bu değerler farklı JVM uygulamaları arasında farklılık gösterebilir.

Java'da bir thread nasıl oluşturulur?

Temel olarak, Java'da bir thread oluşturmanın iki yolu vardır.

İlki, JDK sınıfını java.lang.Thread genişleten bir sınıf yazmak ve bunun yöntemini start() olarak çağırmak:


İkinci yol, java.lang.Runnable arabirimini uygulamak ve bu uygulamayı java.lang.Thread'in yapıcısına bir parametre olarak iletmektir:


1.13 Neden bir thread, yöntemi stop() çağrılarak durdurulmamalıdır?

Java.lang.Thread'in kullanımdan kaldırılan stop() yöntemi kullanılarak bir thread durdurulmamalıdır, çünkü bu yöntemin çağrılması thread'ın edindiği tüm monitörlerin kilidini açmasına neden olur. Serbest bırakılan kilitlerden biri tarafından korunan herhangi bir nesne tutarsız bir durumdaysa, bu durum diğer tüm iş parçacıkları tarafından görülebilir. Bu, diğer iş parçacıkları bu tutarsız nesneyi çalıştırdığında keyfi davranışa neden olabilir.

Bir Thread'i nasıl durdururuz?

Java'da bir thread'i durdurmak için Thread.stop() metodu kullanılabilir, ancak bu yöntem artık önerilmez. Bunun yerine, bir thread'in çalışmasını durdurmak için Thread.interrupt() metodu kullanılmalıdır. Bu yöntem, hedef thread'in interrupt flag'ini ayarlar ve thread uygun bir noktada kendini durdurabilir.

Aşağıdaki örnekte, ana thread bir çalışan thread'i oluşturur ve birkaç saniye bekledikten sonra interrupt() yöntemini kullanarak çalışan thread'in çalışmasını durdurur.


Bir thread'i iki kez başlatmak mümkün mü?

Hayır, start() yöntemini çağırarak bir thread başlattıktan sonra, ikinci bir start() çağrısı bir IllegalThreadStateException oluşturur.

Aşağıdaki kodun çıktısı nedir?


Yukarıdaki kod, "myThread" değil, "main" çıktısını üretir. main() metodunun 2. satırında görüldüğü gibi start() yerine yanlışlıkla run() metodunu çağırıyoruz. Bu nedenle, yeni bir thread başlatılmaz, ancak ana thread içinde run() yöntemi yürütülür.

Arka plan programı (Daemon) thread nedir?

Daemon thread, JVM durup durmamaya karar verdiğinde yürütme durumu değerlendirilmeyen bir thread'dır. JVM, tüm kullanıcı threadleri (deamon threadlerin aksine) sonlandırıldığında durur. Bu nedenle, deamon iş parçacıkları, tüm kullanıcı iş parçacıkları durur durmaz thread JVM tarafından durdurulduğu için, örneğin izleme işlevini uygulamak için kullanılabilir:


Yukarıdaki örnek uygulama, arka plan programı thread sonsuz while döngüsünde arka planda çalışmaya devam etmesine rağmen sonlandırılıyor.

Başlatıldıktan sonra normal bir kullanıcı thread'ı arka plan programı(Deamon)thread'a dönüştürmek mümkün müdür?

Bir kullanıcı thread başlatıldıktan sonra arka plan programı thread'a dönüştürülemez. Halihazırda çalışan bir thread örneğinde thread.setDaemon(true) yöntemini çağırmak, bir IllegalThreadStateException'a neden olur.

Meşgul beklemekten (Busy Waiting) ne anlıyoruz?

Meşgul bekleme, thread'ın/işlemin işlemciyi işgal etmesine izin veren bazı aktif hesaplamalar yaparak bir olayı(event) bekleyen uygulamalar anlamına gelir, ancak programlayıcı tarafından ondan kaldırılabilir. Meşgul beklemeye bir örnek, bekleme zamanını, zamanın belirli bir noktasına ulaşılana kadar tekrar tekrar o anki zamanı belirleyen bir döngü içinde geçirmek olabilir:


Meşgul beklemeyi nasıl önleyebiliriz?

Meşgul beklemeyi önlemenin bir yolu, mevcut thread'ı belirli bir süre uyku moduna geçirmektir. Bu, java.lang.Thread.sleep(long) yöntemini çağırarak, uyku moduna geçen milisaniye sayısını bağımsız değişken olarak ileterek yapılabilir.

Gerçek zamanlı (Real time) işleme için Thread.sleep() kullanabilir miyiz?

Thread.sleep(long) çağrısına geçen milisaniye sayısı, yalnızca zamanlayıcı için geçerli thread'ın ne kadar süreyle yürütülmesi gerekmediğinin bir göstergesidir. Zamanlayıcı, gerçek uygulamaya bağlı olarak thread'ın birkaç milisaniye önce veya sonra yeniden yürütülmesine izin verebilir. Bu nedenle, gerçek zamanlı işleme için Thread.sleep() çağrısı kullanılmamalıdır.

Thread.sleep() kullanılan uyku moduna alınmış bir iş parçacığı, süresi dolmadan nasıl uyandırılabilir?

java.lang.Thread'in interrupt() yöntemi uyuyan bir iş parçacığını kesintiye uğratır. Thread.sleep() çağrılarak uyku moduna alınan kesintiye uğramış iş parçacığı, bir InterruptedException tarafından uyandırılır:


Bir thread interrupt olmuşsa sorgusu nasıl olabilir? 

İş parçacığı, Thread.sleep() gibi bir InterruptedException oluşturacak bir yöntem içinde değilse, iş parçacığı,  java.lang.Thread'den miras aldığı Thread.interrupted() statik yöntemini veya isInterrupted() yöntemini çağırarak kesintiye uğrayıp uğramadığını sorgulayabilir..

Bir InterruptedException nasıl ele alınmalıdır?

sleep() ve join() gibi yöntemler, caller'a bu iş parçacığını başka bir iş parçacağının kesintiye uğrattığını bildirmek için bir InterruptedException atar. Çoğu durumda bu, mevcut iş parçacığına mevcut hesaplamalarını durdurmasını ve beklenmedik bir şekilde bitirmesini söylemek için yapılır. Bu nedenle, istisnayı yakalayarak görmezden gelmek ve sadece konsola veya bazı günlük dosyalarına kaydetmek, genellikle bu tür bir istisnayı ele almanın uygun yolu değildir. Bu özel durumla ilgili sorun, Runnable arabiriminin run() yönteminin run() öğesinin herhangi bir istisna atmasına izin vermemesidir. Yani sadece yeniden atmak yardımcı olmuyor. Bu, run() uygulamasının bu kontrol edilen istisnayı kendisi halletmesi gerektiği anlamına gelir ve bu genellikle istisnanın yakalanmasına ve yok sayılmasına yol açar.


Bir alt iş parçacığı başlattıktan sonra, alt iş parçacığının sonlandırılması için ana iş parçacığında nasıl bekleyeceğiz?

Bir iş parçacığının sonlandırılmasını beklemek, iş parçacığının örnek değişkeninde birleştirme() yöntemi çağrılarak yapılır:



Aşağıdaki programın çıktısı nedir?


Yukarıdaki kodun çıktısı "false". MyDaemonThread örneği bir daemon iş parçacığı olmasına rağmen, join() çağrısı, ana iş parçacığının arka plan programı iş parçacığının yürütülmesi bitene kadar beklemesine neden olur. Dolayısıyla, iş parçacığı örneğinde isAlive() öğesinin çağrılması, arka plan programı iş parçacığının artık çalışmadığını gösterir.

Yakalanmamış bir özel durum run() yönteminden ayrıldığında ne olur? 

Run() yönteminden denetlenmeyen bir istisna kaçabilir. Bu durumda, iş parçacığı Java Sanal Makinesi tarafından durdurulur. UncaughtException Handler arayüzünü bir istisna işleyici olarak uygulayan bir örneği kaydederek bu istisnayı yakalamak mümkündür. Bu, JVM'ye iş parçacığının kendisinde kayıtlı belirli bir işleyici olmaması durumunda sağlanan işleyiciyi kullanmasını söyleyen Thread.setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler) statik yöntemini çağırarak veya setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler) işlevini çağırarak yapılır. iş parçacığı örneğinin kendisidir.

Kapatma kancası nedir? (Shutdown Hook)

Kapatma kancası, JVM kapandığında yürütülen bir iş parçacığıdır. Runtime örneğinde addShutdownHook(Runnable) çağrılarak kaydedilebilir:

Eşzamanlı(synchronized) anahtar kelime hangi amaçlarla kullanılıyor?

Bir kaynağa, bazı statik değerler veya bazı dosya referansları gibi özel erişim uygulamanız gerektiğinde, özel kaynakla çalışan kod, senkronize edilmiş bir blokla kucaklanabilir:


Eşzamanlı bir yöntem (synchronized method) hangi içsel kilidi elde eder?

Eşzamanlı bir yöntem, o yöntemin nesnesi için içsel kilidi alır ve yöntem geri döndüğünde onu serbest bırakır. Yöntem bir istisna oluştursa bile, içsel kilit serbest bırakılır. Bu nedenle, senkronize edilmiş bir yöntem aşağıdaki koda eşittir:

Bir yapıcı (constructor) senkronize edilebilir mi?

Hayır, bir oluşturucu senkronize edilemez. Bunun bir sözdizimi hatasına yol açmasının nedeni, yalnızca inşa edilen iş parçacığının oluşturulan nesneye erişiminin olması gerektiği gerçeğidir.


İlkel değerler(primitive values) içsel kilitler(intrinsic locks) için kullanılabilir mi?

Hayır, ilkel değerler içsel kilitler için kullanılamaz.

İçsel kilitler yeniden girişli midir?

Evet, gerçek kilitlere aynı iş parçacığı tarafından tekrar tekrar erişilebilir. Aksi takdirde, bir kilit alan kod, daha önce edindiği bir kilidi yanlışlıkla almaya çalışmamasına dikkat etmelidir.

Atomik bir işlemden ne anlıyoruz?

Atomik bir işlem, tamamen yürütülen veya hiç yürütülmeyen bir işlemdir.

C++ deyimi atomik mi?

Hayır, bir tamsayı değişkenin artırımı birden fazla işlemden oluşur. Önce c'nin mevcut değerini yüklemeli, artırmalı ve son olarak yeni değeri geri kaydetmeliyiz. Bu artışı gerçekleştiren mevcut iş parçacığı, bu üç adımdan herhangi biri arasında kesintiye uğrayabilir, dolayısıyla bu işlem atomik değildir.


Java'da hangi işlemler atomiktir?

Java dili, atomik olan ve bu nedenle eşzamanlı iş parçacıklarının her zaman aynı değeri görmesini sağlamak için kullanılabilecek bazı temel işlemler sağlar:

• Referans değişkenlere ve ilkel değişkenlere yönelik okuma ve yazma işlemleri (long ve double hariç)

• volatile olarak bildirilen tüm değişkenler için okuma ve yazma işlemleri

Aşağıdaki uygulama iş parçacığı açısından güvenli(thread-safe) mi?


Yukarıdaki kod iş parçacığı için güvenli değil. JIT derleyicisi, senkronize blok içinde örneğin değerini bir kez daha kontrol etse de (performans nedenleriyle), bytecode'u, örneğe yapılan başvuru yapıcı yürütmeyi bitirmeden önce ayarlanacak şekilde yeniden düzenleyebilir. Bu, getInstance() yönteminin tamamen başlatılmamış olabilecek bir nesne döndürdüğü anlamına gelir. Kodu iş parçacığı açısından güvenli hale getirmek için, örnek değişkeni için Java 5'ten beri volatile anahtar sözcüğü kullanılabilir.

Uçucu olarak işaretlenen değişkenler, yalnızca nesnenin yapıcısı yürütmeyi tamamen bitirdiğinde diğer iş parçacıkları tarafından görünür hale gelir.

Kilitlenmeden(deadlock) ne anlıyoruz?

Kilitlenme, iki (veya daha fazla) iş parçacığının diğer iş parçacığında kilitlediği bir kaynağı serbest bırakmak için beklediği, iş parçacığının kendisinin diğer iş parçacığının beklediği bir kaynağı kilitlediği bir durumdur: 

İş Parçacığı 1: A kaynağını kilitler , B kaynağını bekler

İş Parçacığı 2: B kaynağını kilitler, A kaynağını bekler


Kilitlenme durumu için gereksinimler nelerdir?

Genel olarak bir kilitlenme için aşağıdaki gereksinimler tanımlanabilir:

• Karşılıklı dışlama (Mutual exclusion): Herhangi bir zamanda yalnızca bir iş parçacığı tarafından erişilebilen bir kaynak vardır.

• Kaynak tutma(Resource holding:): Bir kaynağı kilitlerken, iş parçacığı başka bir özel kaynak üzerinde başka bir kilit elde etmeye çalışır.

• Önleme yok(No preemption): Bir iş parçacığının kilidi belirli bir süre boyunca tutması durumunda kaynağı serbest bırakan bir mekanizma yoktur.

• Dairesel bekleme(Circular wait): Çalışma zamanı sırasında, kilitlediği bir kaynağı serbest bırakmak için iki (veya daha fazla) iş parçacığının diğer iş parçacığında beklediği bir takımyıldız oluşur.

Kilitlenmeleri tamamen önlemek mümkün mü?

Kilitlenmeleri önlemek için kilitlenme gereksinimlerinden birinin (veya daha fazlasının) ortadan kaldırılması gerekir:

• Karşılıklı dışlama: Bazı durumlarda, iyimser kilitleme kullanarak karşılıklı dışlamayı önlemek mümkündür.

• Kaynak tutma: Bir iş parçacığı, tüm özel kilitleri elde etmeyi başaramadığı zaman, tüm özel kilitlerini serbest bırakabilir.

• Önleme yok: Özel bir kilit için bir zaman aşımı kullanmak, kilidi belirli bir süre sonra serbest bırakır.

• Döngüsel bekleme: Tüm özel kilitler aynı sıradaki tüm iş parçacıkları tarafından elde edildiğinde döngüsel bekleme gerçekleşmez.

Bir kilitlenme tespiti uygulamak mümkün müdür?

Tüm özel kilitler izlendiğinde ve yönlendirilmiş bir grafik olarak modellendiğinde, bir kilitlenme tespit sistemi, kilitlediği bir kaynağı serbest bırakmak için her biri diğer iş parçacığında bekleyen iki iş parçacığını arayabilir. Bekleyen iş parçacıkları daha sonra bir tür istisna tarafından diğer iş parçacığının beklediği kilidi serbest bırakmaya zorlanabilir.

Canlı kilit (livelock) nedir?

Canlı kilit, iki veya daha fazla iş parçacığının başka bir iş parçacığından kaynaklanan bir eyleme yanıt vererek birbirini bloke ettiği bir durumdur. İki veya daha fazla iş parçacığının belirli bir durumda beklediği bir kilitlenme durumunun aksine, canlı kilitlemeye katılan iş parçacıkları durumlarını normal işlerinde ilerlemeyi engelleyecek şekilde değiştirir. Bir örnek, iki iş parçacığının iki kilit elde etmeye çalıştığı, ancak ikinci kilidi elde edemeyince elde ettikleri bir kilidi serbest bıraktığı bir durum olabilir. Artık her iki iş parçacığı aynı anda ilk iş parçacığını almaya çalışabilir. Yalnızca bir iş parçacığı başarılı olduğu için, ikinci iş parçacığı ikinci kilidi elde etmeyi başarabilir. Şimdi her iki iş parçacığı da iki farklı kilide sahiptir, ancak her ikisi de her iki kilide de sahip olmak istedikleri için kilitlerini serbest bırakırlar ve baştan tekrar denerler. Bu durum şimdi tekrar tekrar olabilir.

İş parçacığı açlığından(thread starvation) ne anlıyoruz?

Daha düşük önceliğe sahip iş parçacıkları, daha yüksek önceliğe sahip iş parçacıklarından daha az yürütme süresi alır. Daha düşük önceliğe sahip iş parçacıkları uzun süreli hesaplamalar yaptığında, bu iş parçacıklarının hesaplamalarını tam zamanında bitirmek için yeterli zamanları olmayabilir. Daha yüksek önceliğe sahip iş parçacıkları hesaplama sürelerini çaldığından "açlıktan ölüyor" gibi görünüyorlar.

Senkronize bir blok iş parçacığının aç kalmasına neden olabilir mi?

İş parçacıklarının senkronize bir bloğa girebileceği sıra tanımlanmamıştır. Yani teorik olarak, birçok thread'in senkronize bir bloğa girişi beklemesi durumunda, bazı thread'lerin diğer thread'lerden daha uzun süre beklemesi gerekebilir. Dolayısıyla işlerini zamanında bitirmek için yeterli hesaplama süresine sahip değiller.

Yarış durumu(race condition) teriminden ne anlıyoruz?

Bir yarış koşulu, bazı çoklu iş parçacıklı uygulamanın sonucunun, katılan iş parçacıklarının tam zamanlama davranışına bağlı olduğu takımyıldızları tanımlar. Çoğu durumda bu tür bir davranışa sahip olmak istenmez, bu nedenle yarış durumu terimi aynı zamanda eksik iş parçacığı senkronizasyonundan kaynaklanan bir hatanın farklı sonuçlara yol açtığı anlamına gelir. Bir yarış koşulu için basit bir örnek, bir tamsayı değişkeninin iki eşzamanlı iş parçacığı tarafından artırılmasıdır. İşlem birden fazla tek ve atomik işlemden oluştuğu için, her iki iş parçacığı aynı değeri okur ve artırır. Bu eşzamanlı artıştan sonra, tamsayı değişkeninin miktarı iki değil, yalnızca bir artırılır.

Adil kilitlerden(fair locks) ne anlıyoruz?

Adil bir kilit, engeli bazı özel kaynaklara aşan bir sonraki iş parçacığını seçerken iş parçacıklarının bekleme süresini hesaba katar. Adil kilidin örnek bir uygulaması Java SDK tarafından sağlanır: java.util.concurrent.locks.ReentrantLock. Boole bayrağı true olarak ayarlanmış yapıcı kullanılırsa, ReentrantLock en uzun süre bekleyen iş parçacığına erişim sağlar.

Her nesnenin java.lang.Object'ten miras aldığı hangi iki yöntem, basit bir üretici/tüketici senaryosu uygulamak için kullanılabilir?

Bir çalışan iş parçacığı mevcut görevini bitirdiğinde ve yeni görevler için sıra boş olduğunda, kuyruk nesnesinde içsel bir kilit elde ederek ve wait() yöntemini çağırarak işlemciyi serbest bırakabilir. İş parçacığı, kuyruğa yeni bir görev koyan ve sıra nesnesinde yeniden aynı iç kilidi alan ve üzerinde notify() öğesini çağıran bazı üretici iş parçacığı tarafından uyandırılacaktır.

notify() ve notifyAll() arasındaki fark nedir?

Her iki yöntem de wait() çağrılarak kendilerini uyku moduna almış bir veya daha fazla iş parçacığını uyandırmak için kullanılır. notify() yalnızca bekleyen evrelerden birini uyandırırken, notifyAll() tüm bekleyen evreleri uyandırır.

notify() çağrılarak hangi thread'in uyanacağı nasıl belirlenir?

Birden fazla thread bekliyorsa notify() çağrılarak hangi threadlerin uyandırılacağı belirtilmez. Bu nedenle kod, herhangi bir somut JVM uygulamasına dayanmamalıdır.

Java Concurrency / Multithreading - RAJEEV SINGH

Yazının orjinaline buradan ulaşabilirsiniz. 


Eşzamanlılık, aynı anda birden fazla şeyi yapabilme yeteneğidir.

İlk zamanlarda, bilgisayarlar aynı anda yalnızca bir programı çalıştırabiliyordu. Ancak şimdi, modern bilgisayarlar aynı anda birçok görevi yerine getirebiliyor. Örneğin -

Bloguma bir web tarayıcısında göz atabilir ve aynı zamanda bir medya oynatıcıda müzik dinleyebilirsiniz.


Bir kelime işlemcide bir belgeyi düzenleyebilirsiniz, diğer uygulamalar aynı anda internetten dosya indirebilir.

Eşzamanlılık mutlaka birden fazla uygulamayı içermez. Tek bir uygulamanın birden çok parçasını aynı anda çalıştırmak, eşzamanlılık olarak da adlandırılır. Örneğin -

Bir kelime işlemci metni biçimlendirir ve aynı zamanda klavye olaylarına yanıt verir .


Bir ses akışı uygulaması, sesi ağdan okur, sıkıştırır ve aynı anda ekranı günceller .


Esasen bilgisayarda çalışan bir program olan web sunucusu, aynı anda dünyanın her yerinden gelen binlerce isteğe hizmet eder .

Aynı anda birden fazla iş yapabilen yazılımlara eş zamanlı yazılım denir.

Bilgisayarımın aşağıdaki ekran görüntüsü bir eşzamanlılık örneğini göstermektedir. Bilgisayar sistemim aynı anda birden fazla şey yapıyor - Bir medya oynatıcıda video çalıştırıyor, bir terminalde klavye girişini kabul ediyor ve IntelliJ Idea'da bir proje oluşturuyor.



Eşzamanlılık: Kaputun Altında



Tamam! Bilgisayarların aynı anda birden çok görevi çalıştırabildiğini anlıyorum, ancak bunu nasıl yapıyorlar?

Günümüzde bilgisayarların birden fazla işlemciyle geldiğini biliyorum, ancak tek işlemcili bir sistemde de eşzamanlılık mümkün değil mi? Ayrıca bilgisayarlar, mevcut işlemci sayısından çok daha fazla görevi yerine getirebilir.

Tek bir CPU'da bile birden fazla görev aynı anda nasıl yürütülebilir?

Peki! Görünen o ki, aslında aynı fiziksel anda yürütülmüyorlar. Eşzamanlılık paralel yürütme anlamına gelmez.

“Aynı anda birden fazla görev yürütülüyor” dediğimizde, aslında “birden çok görev aynı zaman diliminde ilerleme kaydediyor” demek istiyoruz.

Görevler aralıklı olarak yürütülür. İşletim sistemi, görevler arasında o kadar sık ​​geçiş yapar ki, kullanıcılara aynı fiziksel anda yürütülüyormuş gibi görünür.



Bu nedenle, Eşzamanlılık Paralellik anlamına gelmez . Aslında, tek işlemcili bir sistemde Paralellik mümkün değildir.

Eşzamanlılık Birimi



Eşzamanlılık çok geniş bir terimdir ve çeşitli düzeylerde kullanılabilir. Örneğin -

Çoklu İşleme(Multiprocessing) - Aynı anda çalışan Çoklu İşlemciler/CPU'lar. Buradaki eşzamanlılık birimi bir CPU'dur.


Çoklu görev(Multitasking) - Tek bir CPU üzerinde aynı anda çalışan birden fazla görev/işlem. İşletim sistemi bu görevleri aralarında çok sık geçiş yaparak yürütür. Bu durumda eşzamanlılık birimi bir Süreçtir.


Çoklu iş parçacığı(Multithreading) - Aynı programın birden çok parçasının aynı anda çalışması. Bu durumda bir adım daha ileri giderek aynı programı birden çok parçaya/iş parçacığına bölüp bu evreleri aynı anda çalıştırıyoruz.

Processes ve Threadler

Şimdi iki temel eşzamanlılık biriminden bahsedelim: İşlemler ve İş Parçacıkları.

İşlem

İşlem, yürütülmekte olan bir programdır. Kendi adres alanı, bir çağrı yığını ve açık dosyalar gibi herhangi bir kaynağa bağlantısı vardır.

Bir bilgisayar sistemi normalde aynı anda çalışan birden çok işleme sahiptir. İşletim sistemi tüm bu işlemlerin kaydını tutar ve CPU'nun işlem süresini aralarında paylaşarak yürütülmesini kolaylaştırır.

Thread

Bir iş parçacığı, bir işlem içindeki bir yürütme yoludur. Her işlemin ana iş parçacığı adı verilen en az bir iş parçacığı vardır. Ana iş parçacığı, işlem içinde ek iş parçacıkları oluşturabilir.

Bir işlem içindeki iş parçacıkları, bellek ve açık dosyalar dahil olmak üzere işlemin kaynaklarını paylaşır. Ancak, her iş parçacığının kendi çağrı yığını vardır.

İş parçacıkları işlemin aynı adres alanını paylaştığından, yeni iş parçacıkları oluşturmak ve aralarında iletişim kurmak daha verimlidir.

Eşzamanlılıkla İlgili Sık Karşılaşılan Sorunlar



Eşzamanlılık, CPU kullanımını artırarak bilgisayarların verimini büyük ölçüde artırır. Ancak harika performansla birlikte birkaç sorun ortaya çıkıyor -

İş parçacığı girişim hataları (Yarış Koşulları) (Race Conditions) : Birden çok iş parçacığı aynı anda paylaşılan bir değişkeni okuyup yazmaya çalıştığında ve bu okuma ve yazma işlemleri yürütme sırasında çakıştığında iş parçacığı girişim hataları oluşur.
  • Bu durumda, nihai sonuç, tahmin edilemez olan okuma ve yazma işlemlerinin gerçekleştiği sıraya bağlıdır. Bu, thread girişim hatalarının tespit edilmesini ve düzeltilmesini zorlaştırır.
    
    Bir seferde yalnızca bir iş parçacığının paylaşılan bir kaynağa erişmesini sağlayarak iş parçacığı girişim hatalarından kaçınılabilir. Bu genellikle herhangi bir paylaşılan kaynağa erişmeden önce birbirini dışlayan bir kilit alarak yapılır.
    
    Herhangi bir paylaşılan kaynağa erişmeden önce bir kilit edinme kavramı, **kilitlenme** ve **açlık** gibi başka sorunlara yol açabilir. Bu sorunları ve çözümlerini gelecek eğitimlerde öğreneceğiz.
  • Bellek tutarlılığı hataları (Memory consistency errors) : Farklı iş parçacıkları aynı verilerin tutarsız görünümlerine sahip olduğunda bellek tutarlılığı hataları oluşur. Bu, bir iş parçacığı bazı paylaşılan verileri güncellediğinde olur, ancak bu güncelleme diğer iş parçacıklarına yayılmaz ve eski verileri kullanırlar.

Java Thread and Runnable


Java'da yeni threadlerin nasıl oluşturulacağını ve bu konuların içinde görevlerin nasıl çalıştırılacağını öğreneceğiz.

Bir Thread Yaratmak ve Başlatmak

Java'da thread oluşturmanın iki yolu vardır.

1.Thread Class'ından Extend Ederek

Sınıfınızı Thread'den genişleterek ve onun run() yöntemini geçersiz kılarak yeni bir iş parçacığı oluşturabilirsiniz.

run() yöntemi, yeni iş parçacığının içinde yürütülen kodu içerir. Bir iş parçacığı oluşturulduktan sonra, start() yöntemini çağırarak başlatabilirsiniz.



public class ThreadExample extends Thread {

    // run() method contains the code that is executed by the thread.
    @Override
    public void run() {
        System.out.println("Inside : " + Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        System.out.println("Inside : " + Thread.currentThread().getName());

        System.out.println("Creating thread...");
        Thread thread = new ThreadExample();

        System.out.println("Starting thread...");
        thread.start();
    }
}




# Output
Inside : main
Creating thread...
Starting thread...
Inside : Thread-0
Thread.currentThread(), o anda yürütülmekte olan iş parçacığına bir başvuru döndürür. Yukarıdaki örnekte, mevcut iş parçacığının adını yazdırmak için iş parçacığının getName() yöntemini kullandım.

Her thread'in bir adı vardır. Thread(String name) yapıcısını kullanarak özel bir adla bir iş parçacığı oluşturabilirsiniz. Herhangi bir ad belirtilmezse, iş parçacığı için otomatik olarak yeni bir ad seçilir.

2-Runnable bir Nesne Yaratarak

Runnable arabirim, bir iş parçacığı tarafından yürütülmesi amaçlanan herhangi bir nesne için birincil şablondur. İş parçacığı tarafından yürütülen kodu içermesi amaçlanan tek bir run() yöntemini tanımlar.

Örneğinin bir iş parçacığı tarafından yürütülmesi gereken herhangi bir sınıf, Runnable arabirimini uygulamalıdır.

Thread sınıfı, Runnable'ı run() yönteminin boş bir uygulamasıyla uygular.

Yeni bir iş parçacığı oluşturmak için, Runnable arabirimini uygulayan sınıfın bir örneğini oluşturun ve ardından bu örneği Thread(Runnable target) yapıcısına iletin.




public class RunnableExample implements Runnable {

    public static void main(String[] args) {
        System.out.println("Inside : " + Thread.currentThread().getName());

        System.out.println("Creating Runnable...");
        Runnable runnable = new RunnableExample();

        System.out.println("Creating Thread...");
        Thread thread = new Thread(runnable);

        System.out.println("Starting Thread...");
        thread.start();
    }

    @Override
    public void run() {
        System.out.println("Inside : " + Thread.currentThread().getName());
    }
}

# Output
Inside : main
Creating Runnable...
Creating Thread...
Starting Thread...
Inside : Thread-0
Runnable'ı uygulayan bir sınıf oluşturmak ve ardından çalıştırılabilir nesneyi almak için bu sınıfı başlatmak yerine, Java'nın anonim sınıf sözdizimini kullanarak anonim bir çalıştırılabilir nesne oluşturabileceğinizi unutmayın.

Anonim sınıflar, kodunuzu daha sade hale getirmenizi sağlar. Aynı anda bir sınıfı bildirmenizi ve başlatmanızı sağlarlar. - Java dokümanından.



public class RunnableExampleAnonymousClass {

    public static void main(String[] args) {
        System.out.println("Inside : " + Thread.currentThread().getName());

        System.out.println("Creating Runnable...");
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("Inside : " + Thread.currentThread().getName());
            }
        };

        System.out.println("Creating Thread...");
        Thread thread = new Thread(runnable);

        System.out.println("Starting Thread...");
        thread.start();
    }
}
Yukarıdaki örnek, Java 8'in lambda ifadesi kullanılarak daha da kısaltılabilir -
  
  public class RunnableExampleLambdaExpression {

    public static void main(String[] args) {
        System.out.println("Inside : " + Thread.currentThread().getName());

        System.out.println("Creating Runnable...");
        Runnable runnable = () -> {
            System.out.println("Inside : " + Thread.currentThread().getName());
        };

        System.out.println("Creating Thread...");
        Thread thread = new Thread(runnable);

        System.out.println("Starting Thread...");
        thread.start();

    }
}

Runnable veya Thread Hangisini Kullanmalı?


Thread sınıfından genişleterek bir iş parçacığı oluşturduğunuz ilk yöntem çok sınırlıdır çünkü sınıfınızı Thread'den bir kez genişlettiğinizde, Java çoklu kalıtıma izin vermediğinden başka bir sınıftan genişletemezsiniz.

Ayrıca, iyi tasarım uygulamasını izlerseniz, Kalıtım, üst sınıfın işlevselliğini genişletmek içindir, ancak bir iş parçacığı oluşturduğunuzda, Thread sınıfının işlevselliğini genişletmezsiniz, yalnızca run() yönteminin uygulanmasını sağlarsınız.

Bu nedenle, genel olarak, bir iş parçacığı oluşturmak için her zaman Runnable nesnesini kullanmalısınız. Bu yöntem daha esnektir. Sınıfınızın başka herhangi bir sınıftan genişlemesine izin verir. Ayrıca, kodunuzu daha özlü hale getirmek için Runnable ile anonim sınıf sözdizimini ve Java 8'in lambda ifadesini kullanabilirsiniz.

Bir Thread'in sleep() Methodu ile Çalışmasına Ara Vermek


Thread sınıfı tarafından sağlanan sleep() yöntemi, o anda yürütülmekte olan iş parçacığının yürütülmesini belirtilen milisaniye sayısı kadar duraklatmanıza olanak tanır.





  

    public class ThreadSleepExample {

    public static void main(String[] args) {
        System.out.println("Inside : " + Thread.currentThread().getName());

        String[] messages = {"If I can stop one heart from breaking,",
                "I shall not live in vain.",
                "If I can ease one life the aching,",
                "Or cool one pain,",
                "Or help one fainting robin",
                "Unto his nest again,",
                "I shall not live in vain"};

        Runnable runnable = () -> {
            System.out.println("Inside : " + Thread.currentThread().getName());

            for(String message: messages) {
                System.out.println(message);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    throw new IllegalStateException(e);
                }
            }
        };

        Thread thread = new Thread(runnable);

        thread.start();
    }
}
    
  

  # Output
Inside : main
Inside : Thread-0
If I can stop one heart from breaking,
I shall not live in vain.
If I can ease one life the aching,
Or cool one pain,
Or help one fainting robin
Unto his nest again,
I shall not live in vain
    

Yukarıdaki örnek, ileti dizisi üzerinde yinelenen, mevcut iletiyi yazdıran, Thread.sleep() öğesini çağırarak 2 saniye bekleyen ve ardından bir sonraki yinelemeye geçen bir for döngüsünden oluşur.

Herhangi bir iş parçacığı geçerli iş parçacığını keserse, sleep() yöntemi InterruptedException'ı atar. InterruptedException, kontrol edilen bir istisnadır ve ele alınması gerekir.


join() Kullanarak Diğer Thread'in Tamamlanmasını Beklemek

Join() yöntemi, bir iş parçacığının diğerinin tamamlanmasını beklemesine izin verir. Aşağıdaki örnekte, Thread 2, Thread.join(1000)'i çağırarak 1000 milisaniye boyunca Thread 1'in tamamlanmasını bekler ve ardından yürütmeyi başlatır -


  

      public class ThreadJoinExample {

    public static void main(String[] args) {
        // Create Thread 1
        Thread thread1 = new Thread(() -> {
            System.out.println("Entered Thread 1");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new IllegalStateException(e);
            }
            System.out.println("Exiting Thread 1");
        });

        // Create Thread 2
        Thread thread2 = new Thread(() -> {
            System.out.println("Entered Thread 2");
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                throw new IllegalStateException(e);
            }
            System.out.println("Exiting Thread 2");
        });

        System.out.println("Starting Thread 1");
        thread1.start();

        System.out.println("Waiting for Thread 1 to complete");
        try {
            thread1.join(1000);
        } catch (InterruptedException e) {
            throw new IllegalStateException(e);
        }

        System.out.println("Waited enough! Starting Thread 2 now");
        thread2.start();
    }
}
                
Starting Thread 1
Waiting for Thread 1 to complete
Entered Thread 1
Waited enough! Starting Thread 2 now
Entered Thread 2
Exiting Thread 1
Exiting Thread 2

Thread.join() için bekleme süresi MIN (iş parçacığının sonlanması için geçen süre, yöntem bağımsız değişkeninde belirtilen milisaniye sayısı) değerine eşittir.

Join() yöntemi, argüman olmadan da çağrılabilir. Bu durumda, sadece iş parçacığı ölene kadar bekler.

Rastgele İçerik

DonanımHaber

© tüm hakları saklıdır
made with by templateszoo