java synchronized what is thread synchronization java
Bu Eğitimde Java'da İş Parçacığı Senkronizasyonu, Java Kilidi, Yarış Durumu, Mutex, Java Uçucu ve Kilitlenme gibi Java'daki İlgili Kavramları Açıklar:
Birden çok iş parçacığının dahil olduğu çok iş parçacıklı bir ortamda, birden fazla iş parçacığı aynı kaynağı aynı anda almaya çalıştığında, çakışmalar olması kaçınılmazdır. Bu çatışmalar 'yarış durumuna' neden olur ve bu nedenle program beklenmedik sonuçlar üretir.
Örneğin, tek bir dosya iki iş parçacığı tarafından güncelleniyor. Bir T1 iş parçacığı bu dosyayı güncelleme sürecinde ise, bir değişken söyleyin. Şimdi T1 ile yapılan bu güncelleme devam ederken, diyelim ki ikinci iş parçacığı T2 de aynı değişkeni güncelledi. Bu şekilde değişken yanlış sonuçlar verecektir.
=> Tam Java Eğitim Serisini Buradan İzleyin.
Birden fazla iş parçacığı dahil edildiğinde, bu iş parçacıklarını bir kaynağa bir seferde tek bir iş parçacığı ile erişilebilecek şekilde yönetmeliyiz. Yukarıdaki örnekte, her iki iş parçacığı tarafından erişilen dosya, T1 erişimi tamamlanana kadar T2'nin dosyaya erişemeyeceği şekilde yönetilmelidir.
Bu, Java'da ' Konu Senkronizasyonu ”.
Ne öğreneceksin:
- Java'da İş Parçacığı Senkronizasyonu
- Senkronizasyon Olmadan Çoklu İş Parçacığı
- Senkronizasyon ile Çoklu iş parçacığı
- Sonuç
Java'da İş Parçacığı Senkronizasyonu
Java çok iş parçacıklı bir dil olduğundan, bir uygulamada birden çok iş parçacığı paralel olarak çalıştırıldığı için iş parçacığı senkronizasyonu Java'da büyük önem taşır.
Anahtar kelimeler kullanıyoruz 'Senkronize' ve 'uçucu' Java'da Senkronizasyona ulaşmak için
Paylaşılan nesne veya kaynak değiştirilebilir olduğunda senkronizasyona ihtiyacımız var. Kaynak değişmez ise, bu durumda evreler kaynağı yalnızca eşzamanlı veya tek tek okur.
Bu durumda kaynağı senkronize etmemize gerek yoktur. Bu durumda JVM, Java ile senkronize edilmiş kod, her seferinde bir iş parçacığı tarafından yürütülür .
Çoğu zaman, Java'da paylaşılan kaynaklara eşzamanlı erişim, 'Bellek tutarsızlığı' ve 'iş parçacığı çakışması' gibi hatalara neden olabilir. Bu hatalardan kaçınmak için, paylaşılan kaynakların senkronizasyonuna gitmemiz gerekir, böylece bu kaynaklara erişim karşılıklı olarak dışlanır.
Adlı bir kavram kullanıyoruz Senkronizasyonu uygulamak için izler. Bir monitöre bir seferde yalnızca bir iş parçacığı ile erişilebilir. Bir iplik kilidi aldığında, ipliğin monitöre girdiğini söyleyebiliriz.
Bir monitöre belirli bir iş parçacığı ile erişildiğinde, monitör kilitlenir ve monitöre girmeye çalışan diğer tüm iş parçacıkları, erişim iş parçacığı bitene ve kilidi serbest bırakana kadar askıya alınır.
İleride, bu eğiticide Java'da senkronizasyonu ayrıntılı olarak tartışacağız. Şimdi, Java'da senkronizasyonla ilgili bazı temel kavramları tartışalım.
Java'da Irk Durumu
Çok iş parçacıklı bir ortamda, birden fazla iş parçacığı aynı anda yazmak için paylaşılan bir kaynağa erişmeye çalıştığında, birden çok iş parçacığı kaynağa erişimi tamamlamak için birbiriyle yarışır. Bu, 'ırk durumu' na yol açar.
Dikkate alınması gereken bir nokta, birden fazla iş parçacığı yalnızca okumak için paylaşılan bir kaynağa erişmeye çalışıyorsa hiçbir sorun olmamasıdır. Sorun, birden çok iş parçacığının aynı kaynağa aynı anda erişmesi durumunda ortaya çıkar.
Yarış koşulları, programdaki iş parçacıklarının düzgün senkronizasyonunun olmaması nedeniyle oluşur. Bir seferde sadece bir iş parçacığı kaynağa erişecek ve yarış koşulu sona erecek şekilde evreleri düzgün bir şekilde senkronize ettiğimizde.
Peki Yarış Durumunu nasıl tespit ederiz?
Yarış durumunu tespit etmenin en iyi yolu kod incelemesidir. Bir programcı olarak, oluşabilecek potansiyel yarış koşullarını kontrol etmek için kodu baştan sona incelemeliyiz.
Java'da Kilitler / Monitörler
Senkronizasyonu uygulamak için monitörler veya kilitler kullandığımızdan daha önce bahsetmiştik. Monitör veya kilit, dahili bir varlıktır ve her nesneyle ilişkilendirilmiştir. Dolayısıyla, bir iş parçacığının nesneye erişmesi gerektiğinde, önce nesnesinin kilidini veya monitörünü alması, nesne üzerinde çalışması ve ardından kilidi serbest bırakması gerekir.
Java'daki kilitler aşağıda gösterildiği gibi görünecektir:
public class Lock { private boolean isLocked = false; public synchronized void lock() throws InterruptedException { while(isLocked) { wait(); } isLocked = true; } public synchronized void unlock(){ isLocked = false; notify(); } }
Yukarıda gösterildiği gibi, örneği kilitleyen bir lock () yöntemimiz var. Lock () yöntemini çağıran tüm iş parçacıkları, unblock () yöntem kümeleri kilitlenene ve tüm bekleyen iş parçacıklarını bilgilendirene kadar engellenecektir.
Kilitler hakkında hatırlanması gereken bazı ipuçları:
- Java'da her nesnenin bir kilidi veya bir monitörü vardır. Bu kilide bir diş ile erişilebilir.
- Bir seferde sadece bir iş parçacığı bu monitörü veya kilidi alabilir.
- Java programlama dili, Senkronize olarak bir blok veya yöntem yaparak iş parçacıklarını senkronize etmemize olanak tanıyan bir Senkronize anahtar sözcüğü sağlar.
- İş parçacığının erişmesi gereken paylaşılan kaynaklar bu Senkronize blok / yöntem altında tutulur.
Java'da Muteksler
Çok iş parçacıklı bir ortamda, birden fazla iş parçacığı paylaşılan kaynaklara aynı anda erişmeye çalıştığında ve yarış koşullarının beklenmedik çıktılarla sonuçlandığında yarış koşullarının ortaya çıkabileceğini zaten tartışmıştık.
Programın paylaşılan kaynağa erişmeye çalışan kısmına 'Kritik Bölüm' . Yarış koşullarının oluşmasını önlemek için, kritik bölüme erişimi senkronize etme ihtiyacı vardır. Bu kritik bölümü senkronize ederek, bir seferde yalnızca bir iş parçacığının kritik bölüme erişebilmesini sağlıyoruz.
En basit eşzamanlayıcı türü 'muteks' tir. Mutex, herhangi bir örnekte kritik bölümü yalnızca bir iş parçacığının çalıştırabilmesini sağlar.
Muteks, yukarıda tartıştığımız monitörler veya kilitler kavramına benzer. Bir iş parçacığının kritik bir bölüme erişmesi gerekiyorsa, muteksi edinmesi gerekir. Muteks elde edildikten sonra, iş parçacığı kritik bölüm koduna erişecek ve bittiğinde muteksi serbest bırakacaktır.
Kritik bölüme erişmek için bekleyen diğer konular bu arada bloke edilecektir. İş parçacığı tutan muteks onu serbest bırakır bırakmaz, başka bir iş parçacığı kritik bölüme girecektir.
informatica mülakat soruları ve cevapları 5 yıllık deneyim
Java'da bir muteks gerçekleştirmenin birkaç yolu vardır.
- Senkronize Anahtar Kelimeyi Kullanma
- Semafor kullanma
- ReentrantLock'u kullanma
Bu eğitimde, ilk yaklaşımı, yani Senkronizasyon'u tartışacağız. Diğer iki yaklaşım - Semafor ve ReentrantLock, java eşzamanlı paketini tartışacağımız bir sonraki derste tartışılacaktır.
Senkronize Anahtar Kelime
Java, Kritik bölümü işaretlemek için bir programda kullanılabilecek bir 'Eşitlenmiş' anahtar sözcüğü sağlar. Kritik bölüm, bir kod bloğu veya eksiksiz bir yöntem olabilir. Bu nedenle, Senkronize anahtar sözcüğü ile işaretlenen kritik bölüme yalnızca bir iş parçacığı erişebilir.
Synchronized anahtar sözcüğünü kullanarak bir uygulama için eşzamanlı bölümleri (eşzamanlı olarak çalışan bölümler) yazabiliriz. Ayrıca bir kod bloğu veya Senkronize edilmiş bir yöntem yaparak yarış koşullarından kurtuluyoruz.
Bir bloğu veya yöntemi senkronize olarak işaretlediğimizde, bu varlıklar içindeki paylaşılan kaynakları eşzamanlı erişimden ve dolayısıyla bozulmadan koruruz.
Senkronizasyon Türleri
Aşağıda açıklandığı gibi 2 tür senkronizasyon vardır:
# 1) Süreç Senkronizasyonu
Süreç Senkronizasyonu, aynı anda çalışan birden çok işlemi veya iş parçacığı içerir. Nihayetinde, bu süreçlerin veya iş parçacığının belirli bir eylem dizisine bağlı olduğu bir duruma ulaşırlar.
# 2) Konu Senkronizasyonu
İş Parçacığı Senkronizasyonunda, birden fazla iş parçacığı paylaşılan bir alana erişmeye çalışıyor. İş parçacıkları, paylaşılan alana bir seferde yalnızca bir iş parçacığı tarafından erişilecek şekilde senkronize edilir.
İşlem Senkronizasyonu bu öğreticinin kapsamı dışındadır. Bu nedenle, burada sadece İş Parçacığı Senkronizasyonunu tartışacağız.
Java'da, senkronize edilmiş anahtar kelimeyi şu şekilde kullanabiliriz:
- Bir kod bloğu
- Bir metod
Yukarıdaki türler, birbirini dışlayan evre senkronizasyon türleridir. Karşılıklı dışlama, paylaşılan verilere erişen iş parçacıklarının birbirine karışmasını engeller.
İş parçacığı senkronizasyonunun diğer türü, iş parçacıkları arasındaki işbirliğine dayanan 'InterThread iletişimidir'. Interthread iletişimi bu öğreticinin kapsamı dışındadır.
Blokların ve yöntemlerin senkronizasyonuna geçmeden önce, senkronizasyon olmadığında iş parçacıkları davranışını göstermek için bir Java programı uygulayalım.
Senkronizasyon Olmadan Çoklu İş Parçacığı
Aşağıdaki Java programı, senkronize edilmemiş birden çok iş parçacığına sahiptir.
class PrintCount { //method to print the thread counter public void printcounter() { try { for(int i = 5; i > 0; i--) { System.out.println('Counter ==> ' + i ); } } catch (Exception e) { System.out.println('Thread interrupted.'); } } } //thread class class ThreadCounter extends Thread { private Thread t; private String threadName; PrintCount PD; //class constructor for initialization ThreadCounter( String name, PrintCount pd) { threadName = name; PD = pd; } //run method for thread public void run() { PD.printcounter(); System.out.println('Thread ' + threadName + ' exiting.'); } //start method for thread public void start () { System.out.println('Starting ' + threadName ); if (t == null) { t = new Thread (this, threadName); t.start (); } } } public class Main { public static void main(String args()) { PrintCount PD = new PrintCount(); //create two instances of thread class ThreadCounter T1 = new ThreadCounter( 'ThreadCounter_1 ', PD ); ThreadCounter T2 = new ThreadCounter( 'ThreadCounter_2 ', PD ); //start both the threads T1.start(); T2.start(); // wait for threads to end try { T1.join(); T2.join(); } catch ( Exception e) { System.out.println('Interrupted'); } } }
Çıktı
Çıktıdan, evreler senkronize olmadığından çıktının tutarsız olduğunu görebiliriz. Her iki konu da başlar ve ardından sayacı birbiri ardına görüntüler. Her iki iplik de sonunda çıkar.
Verilen programdan, sayaç değerleri görüntülendikten sonra ilk iş parçacığı çıkılmış ve ardından ikinci iş parçacığı sayaç değerlerini göstermeye başlamış olmalıdır.
Şimdi senkronizasyona gidelim ve kod bloğu senkronizasyonuyla başlayalım.
dvd'yi sabit sürücüye ücretsiz kopyala
Senkronize Kod Bloğu
Senkronize bir blok, bir kod bloğunu senkronize etmek için kullanılır. Bu blok genellikle birkaç satırdan oluşur. Senkronize bir blok, tüm yöntemin senkronize edilmesini istemediğimizde kullanılır.
Örneğin, 75 satır kodlu bir yöntemimiz var. Bunun dışında, bir seferde bir iş parçacığı tarafından yalnızca 10 satır kod çalıştırılması gerekir. Bu durumda tüm metodu senkronize hale getirirsek sisteme yük olacaktır. Bu gibi durumlarda, senkronize bloklara gidiyoruz.
Eşitlenmiş yöntemin kapsamı her zaman eşitlenmiş bir yönteminkinden daha küçüktür. Senkronize bir yöntem, birden çok iş parçacığı tarafından kullanılacak bir paylaşılan kaynağın nesnesini kilitler.
Senkronize bir bloğun genel sözdizimi aşağıda gösterildiği gibidir:
synchronized (lock_object){ //synchronized code statements }
Burada 'kilit_nesnesi', kilidin elde edileceği bir nesne referans ifadesidir. Dolayısıyla, bir iş parçacığı yürütme için blok içindeki senkronize ifadelere erişmek istediğinde, 'kilit_nesne' monitöründeki kilidi edinmelidir.
Daha önce tartışıldığı gibi, senkronize edilmiş anahtar kelime, bir seferde sadece bir iş parçacığının bir kilit alabilmesini ve diğer tüm iş parçacıklarının kilidi tutan iş parçacığı bitene ve kilidi serbest bırakana kadar beklemek zorunda kalmasını sağlar.
Not
- Kullanılan lock_object Null ise bir 'NullPointerException' atılır.
- Kilidi tutmaya devam ederken bir iplik uyursa, kilit açılmaz. Diğer iş parçacıkları bu uyku süresi boyunca paylaşılan nesneye erişemeyecek.
Şimdi, küçük değişikliklerle zaten uygulanmış olan yukarıdaki örneği sunacağız. Önceki programda kodu senkronize etmedik. Şimdi senkronize bloğu kullanacağız ve çıktıyı karşılaştıracağız.
Senkronizasyon ile Çoklu iş parçacığı
Aşağıdaki Java programında senkronize bir blok kullanıyoruz. Run yönteminde, her iş parçacığı için sayacı yazdıran satırların kodunu senkronize ederiz.
class PrintCount { //print thread counter public void printCounter() { try { for(int i = 5; i > 0; i--) { System.out.println('Counter ==> ' + i ); } } catch (Exception e) { System.out.println('Thread interrupted.'); } } } //thread class class ThreadCounter extends Thread { private Thread t; private String threadName; PrintCount PD; //class constructor for initialization ThreadCounter( String name, PrintCount pd) { threadName = name; PD = pd; } //run () method for thread with synchronized block public void run() { synchronized(PD) { PD.printCounter(); } System.out.println('Thread ' + threadName + ' exiting.'); } //start () method for thread public void start () { System.out.println('Starting ' + threadName ); if (t == null) { t = new Thread (this, threadName); t.start (); } } } public class Main { public static void main(String args()) { PrintCount PD = new PrintCount(); //create thread instances ThreadCounter T1 = new ThreadCounter( 'Thread_1 ', PD ); ThreadCounter T2 = new ThreadCounter( 'Thread_2 ', PD ); //start both the threads T1.start(); T2.start(); // wait for threads to end try { T1.join(); T2.join(); } catch ( Exception e) { System.out.println('Interrupted'); } } }
Çıktı
Şimdi senkronize blok kullanan bu programın çıktısı oldukça tutarlı. Beklendiği gibi, her iki iş parçacığı da çalışmaya başlar. İlk iş parçacığı, sayaç değerlerini görüntülemeyi bitirdi ve çıktı. Daha sonra ikinci iş parçacığı, sayaç değerlerini görüntüler ve çıkar.
Senkronize Yöntem
Bu bölümde senkronize edilmiş yöntemi tartışalım. Daha önce, daha az kod satırından oluşan küçük bir bloğu senkronize bir blok olarak ilan edebileceğimizi görmüştük. Tüm fonksiyonun senkronize edilmesini istiyorsak, bir metodu senkronize edilmiş olarak ilan edebiliriz.
Bir yöntem senkronize edildiğinde, bir seferde yalnızca bir iş parçacığı bir yöntem çağrısı yapabilir.
Senkronize bir yöntem yazmak için genel sözdizimi şöyledir:
synchronized method_name (parameters){ //synchronized code }
Senkronize bir blok gibi, senkronize bir metot durumunda, senkronize metoda erişen evreler tarafından kullanılacak bir lock_object'e ihtiyacımız var.
Senkronize yöntem için, kilit nesnesi aşağıdakilerden biri olabilir:
- Senkronize yöntem statikse, kilit nesnesi '.class' nesnesiyle verilir.
- Statik olmayan bir yöntem için, kilit nesnesi, mevcut nesne, yani 'bu' nesne tarafından verilir.
Senkronize edilmiş anahtar kelimenin kendine özgü bir özelliği, yeniden girilmiş olmasıdır. Bu, senkronize bir yöntemin aynı kilitle başka bir senkronize edilmiş yöntemi çağırabileceği anlamına gelir. Böylece kilidi tutan bir iplik, farklı bir kilit elde etmek zorunda kalmadan başka bir senkronize edilmiş yönteme erişebilir.
Senkronize Yöntem aşağıdaki örnek kullanılarak gösterilmektedir.
class NumberClass { //synchronized method to print squares of numbers synchronized void printSquares(int n) throws InterruptedException { //iterate from 1 to given number and print the squares at each iteration for (int i = 1; i <= n; i++) { System.out.println(Thread.currentThread().getName() + ' :: '+ i*i); Thread.sleep(500); } } } public class Main { public static void main(String args()) { final NumberClass number = new NumberClass(); //create thread Runnable thread = new Runnable() { public void run() { try { number.printSquares(3); } catch (InterruptedException e) { e.printStackTrace(); } } }; //start thread instance new Thread(thread, 'Thread One').start(); new Thread(thread, 'Thread Two').start(); } }
Çıktı
Yukarıdaki programda, bir sayının karelerini yazdırmak için senkronize bir yöntem kullandık. Sayının üst sınırı, yönteme argüman olarak aktarılır. Daha sonra 1'den başlayarak, üst sınıra ulaşılana kadar her sayının kareleri yazdırılır.
Ana işlevde, iş parçacığı örneği oluşturulur. Her iş parçacığı örneğine kareleri yazdırmak için bir sayı iletilir.
Yukarıda bahsedildiği gibi, senkronize edilecek bir yöntem statik olduğunda, kilit nesnesi sınıfa dahil olur, nesneye değil. Bu, nesneye değil sınıfa kilitleneceğimiz anlamına gelir. Buna statik senkronizasyon denir.
Aşağıda başka bir örnek verilmiştir.
class Table{ //synchronized static method to print squares of numbers synchronized static void printTable(int n){ for(int i=1;i<=10;i++){ System.out.print(n*i + ' '); try{ Thread.sleep(400); }catch(Exception e){} } System.out.println(); } } //thread class Thread_One class Thread_One extends Thread{ public void run(){ Table.printTable(2); } } //thread class Thread_Two class Thread_Two extends Thread{ public void run(){ Table.printTable(5); } } public class Main{ public static void main(String t()){ //create instances of Thread_One and Thread_Two Thread_One t1=new Thread_One (); Thread_Two t2=new Thread_Two (); //start each thread instance t1.start(); t2.start(); } }
Çıktı
Yukarıdaki programda, sayıların çarpım tablolarını yazdırıyoruz. Tablosu yazdırılacak her numara, farklı iplik sınıfının bir iplik örneğidir. Böylece, 2 ve 5'in çarpım tablolarını yazdırırız, bu nedenle sırasıyla 2 ve 5 numaralı tabloları yazdırmak için iki sınıf 'thread_one ve thread_two' olur.
Özetlemek gerekirse, Java ile senkronize edilmiş anahtar sözcük aşağıdaki işlevleri yerine getirir:
- Java'daki senkronize edilmiş anahtar sözcük, bir kilitleme mekanizması sağlayarak paylaşılan kaynaklara karşılıklı olarak dışlayıcı erişimi garanti eder. Kilitleme ayrıca yarış koşullarını da engeller.
- Senkronize anahtar kelimeyi kullanarak kodda eşzamanlı programlama hatalarını önleriz.
- Bir yöntem veya blok senkronize edilmiş olarak bildirildiğinde, bir iş parçacığının senkronize edilmiş metodu veya bloğu girmek için özel bir kilide ihtiyacı vardır. Gerekli eylemleri gerçekleştirdikten sonra, iş parçacığı kilidi serbest bırakır ve yazma işlemini temizler. Bu şekilde tutarsızlıkla ilgili bellek hatalarını ortadan kaldıracaktır.
Java'da Uçucu
Java'da uçucu bir anahtar sözcük, sınıfları iş parçacığı açısından güvenli hale getirmek için kullanılır. Değişken değerini farklı iş parçacıklarıyla değiştirmek için volatile anahtar kelimesini de kullanırız. Uçucu bir anahtar sözcük, nesnelerin yanı sıra ilkel türlerle bir değişken bildirmek için kullanılabilir.
Bazı durumlarda, senkronize anahtar kelimeye alternatif olarak geçici bir anahtar kelime kullanılır, ancak bunun senkronize edilmiş anahtar kelimenin yerine geçmediğini unutmayın.
Bir değişken geçici olarak bildirildiğinde, değeri hiçbir zaman önbelleğe alınmaz, ancak her zaman ana bellekten okunur. Uçucu bir değişken, siparişi ve görünürlüğü garanti eder. Bir değişken uçucu olarak tanımlanabilse de, sınıfları veya yöntemleri uçucu olarak bildiremeyiz.
Aşağıdaki kod bloğunu düşünün:
class ABC{ static volatile int myvar =10; }
Yukarıdaki kodda, myvar değişkeni statik ve uçucudur. Statik bir değişken, tüm sınıf nesneleri arasında paylaşılır. Uçucu değişken her zaman ana bellekte bulunur ve asla önbelleğe alınmaz.
Dolayısıyla ana bellekte sadece bir myvar kopyası olacak ve tüm okuma / yazma işlemleri ana bellekten bu değişken üzerinde yapılacaktır. Myvar uçucu olarak bildirilmemişse, her iş parçacığı nesnesinin tutarsızlıklara neden olacak farklı bir kopyası olacaktır.
Geçici ve Senkronize anahtar kelimeler arasındaki bazı farklar aşağıda listelenmiştir.
Uçucu Anahtar Kelime | Senkronize anahtar kelime |
---|---|
Volatile anahtar kelime yalnızca değişkenlerle kullanılır. | Senkronize anahtar kelime, kod blokları ve yöntemlerle kullanılır. |
Uçucu bir anahtar kelime iş parçacığını beklemeye engelleyemez. | Senkronize edilmiş anahtar kelime iş parçacığını beklemeye engelleyebilir. |
Volatile ile iplik performansı artırıldı. | İplik performansı, senkronize edildiğinde biraz düşer. |
Uçucu değişkenler ana bellekte bulunur. | Senkronize yapılar ana bellekte bulunmaz. |
Volatile, bir seferde iş parçacığı belleği ile ana bellek arasında bir değişkeni senkronize eder. | Senkronize anahtar kelime, tüm değişkenleri aynı anda senkronize eder. |
Java'da Kilitlenme
Eşitlenmiş anahtar sözcük kullanarak birden çok iş parçacığını eşitleyebileceğimizi ve programları iş parçacığı için güvenli hale getirebileceğimizi gördük. İş parçacıkları senkronize ederek, birden çok iş parçacığının çok iş parçacıklı bir ortamda aynı anda yürütülmesini sağlıyoruz.
Bununla birlikte, bazen iş parçacıklarının artık aynı anda çalışamayacağı bir durum ortaya çıkar. Bunun yerine hiç durmadan beklerler. Bu, bir iş parçacığı bir kaynakta beklediğinde ve bu kaynak ikinci iş parçacığı tarafından engellendiğinde oluşur.
İkinci iş parçacığı ise birinci iş parçacığı tarafından bloke edilen kaynağı beklemektedir. Böyle bir durum Java'da 'çıkmaza' neden olur.
Java'daki kilitlenme, aşağıdaki görüntü kullanılarak tasvir edilmiştir.
Yukarıdaki diyagramdan görebileceğimiz gibi, A evresi r1 kaynağını kilitledi ve r2 kaynağını bekliyor. B iş parçacığı ise r2 kaynağını bloke etti ve r1'i bekliyor.
Bu nedenle, iş parçacıklarından hiçbiri bekleyen kaynakları ele geçirmedikçe yürütmeyi bitiremez. Bu durum, her iki iş parçacığının kaynaklar için sonsuzca beklediği bir çıkmaza neden oldu.
Aşağıda Java'da bir Deadlock örneği verilmiştir.
public class Main { public static void main(String() args) { //define shared resources final String shared_res1 = 'Java tutorials'; final String shared_res2 = 'Multithreading'; // thread_one => locks shared_res1 then shared_res2 Thread thread_one = new Thread() { public void run() { synchronized (shared_res1) { System.out.println('Thread one: locked shared resource 1'); try { Thread.sleep(100);} catch (Exception e) {} synchronized (shared_res2) { System.out.println('Thread one: locked shared resource 2'); } } } }; // thread_two=> locks shared_res2 then shared_res1 Thread thread_two = new Thread() { public void run() { synchronized (shared_res2) { System.out.println('Thread two: locked shared resource 2'); try { Thread.sleep(100);} catch (Exception e) {} synchronized (shared_res1) { System.out.println('Thread two: locked shared resource 1'); } } } }; //start both the threads thread_one.start(); thread_two.start(); } }
Çıktı
Yukarıdaki programda, iki paylaşılan kaynağımız ve iki iş parçacığımız var. Her iki iş parçacığı da paylaşılan kaynaklara birer birer erişmeye çalışır. Çıktı, her iki iş parçacığını da diğerlerini beklerken bir kaynağı kilitlerken gösterir. Böylece bir kilitlenme durumu yaratır.
Çıkmaz durumların tamamen ortaya çıkmasını engelleyemesek de, bazı adımlar atarak bunlardan kesinlikle kaçınabiliriz.
Aşağıda, Java'daki kilitlenmeleri önleyebileceğimiz araçlar listelenmiştir.
# 1) İç içe geçmiş kilitlerden kaçınarak
İç içe geçmiş kilitlere sahip olmak, kilitlenmelerin en önemli nedenidir. İç içe kilitler, birden çok iş parçacığına verilen kilitlerdir. Bu yüzden birden fazla ipliğe kilit vermekten kaçınmalıyız.
# 2) İş parçacığı birleştirmeyi kullanın
Thread.join'i maksimum süre ile kullanmalıyız, böylece evreler maksimum yürütme süresini kullanabilir. Bu, çoğunlukla bir iş parçacığı sürekli olarak diğerlerini beklerken oluşan kilitlenmeyi önleyecektir.
# 3) Gereksiz kilitlemekten kaçının
Yalnızca gerekli kodu kilitlemeliyiz. Kod için gereksiz kilitlere sahip olmak programda kilitlenmelere neden olabilir. Kilitlenmeler kodu kırabileceğinden ve programın akışını engelleyebileceğinden, programlarımızda kilitlenmelerden kaçınmaya meyilli olmalıyız.
Sıkça Sorulan Sorular
S # 1) Senkronizasyon nedir ve neden önemlidir?
Cevap: Senkronizasyon, paylaşılan bir kaynağın birden çok iş parçacığına erişimini kontrol etme işlemidir. Senkronizasyon olmadan, birden fazla iş parçacığı paylaşılan kaynağı aynı anda güncelleyebilir veya değiştirerek tutarsızlıklara neden olabilir.
Bu nedenle, çok iş parçacıklı bir ortamda, iş parçacıklarının, paylaşılan kaynaklara erişme şekillerinin birbirini dışlayan ve tutarlı olacak şekilde senkronize olmasını sağlamalıyız.
S # 2) Java'da Eşitleme ve Eşitlememe Nedir?
Cevap: Senkronizasyon, yapının iş parçacığı için güvenli olduğu anlamına gelir. Bu, birden çok iş parçacığının yapıya (kod bloğu, yöntem, vb.) Aynı anda erişemeyeceği anlamına gelir.
Eşitlenmemiş yapılar iş parçacığı açısından güvenli değildir. Birden çok iş parçacığı, senkronize edilmemiş yöntemlere veya bloklara herhangi bir zamanda erişebilir. Java'da popüler bir senkronize olmayan sınıf StringBuilder'dır.
S # 3) Senkronizasyon neden gereklidir?
Cevap: Süreçlerin aynı anda yürütülmesi gerektiğinde, senkronizasyona ihtiyacımız var. Bunun nedeni, birçok süreç arasında paylaşılabilecek kaynaklara ihtiyacımız olmasıdır.
Paylaşılan kaynaklara erişim için süreçler veya iş parçacıkları arasındaki çatışmaları önlemek için, bu kaynakları senkronize etmemiz gerekir, böylece tüm iş parçacıkları kaynaklara erişebilir ve uygulama da sorunsuz çalışır.
S # 4) Senkronize DiziListesini nasıl elde edersiniz?
Cevap: ArrayList'i senkronize bir listeye dönüştürmek için, Collections.synchronized list metodunu argüman olarak ArrayList ile birlikte kullanabiliriz.
S # 5) HashMap Senkronize Edildi mi?
c ++ 'da normal ifade
Cevap: Hayır, HashMap senkronize edilmez ancak HashTable senkronize edilir.
Sonuç
Bu eğitimde, konuların senkronizasyonunu ayrıntılı olarak tartıştık. Bununla birlikte, Java'daki geçici anahtar kelime ve kilitlenmeleri de öğrendik. Senkronizasyon, İşlem ve İş Parçacığı senkronizasyonundan oluşur.
Çok iş parçacıklı bir ortamda, iş parçacığı senkronizasyonu ile daha çok ilgileniyoruz. Burada iş parçacığı senkronizasyonunun senkronize anahtar kelime yaklaşımını gördük.
Kilitlenme, birden çok iş parçacığının kaynakları sonsuza kadar beklediği bir durumdur. Java'daki kilitlenmeleri önleme yöntemlerinin yanı sıra Java'daki kilitlenme örneğini gördük.
=> Java'yı Sıfırdan Öğrenmek İçin Burayı Ziyaret Edin.
Önerilen Kaynaklar
- Thread.Sleep () - Örneklerle Java'da Thread Sleep () Yöntemi
- Yöntemler ve Yaşam Döngüsü İçeren Java Konuları
- Java Temel Bilgileri: Java Sözdizimi, Java Sınıfı ve Temel Java Kavramları
- Java'da Çoklu Okuma - Örneklerle Eğitim
- Örneklerle C ++ 'da Çoklu Okuma
- Yeni Başlayanlar İçin JAVA Eğitimi: 100+ Uygulamalı Java Video Eğitimi
- Java Bileşenleri: Java Platformu, JDK, JRE ve Java Sanal Makinesi
- Java Dize Eğitimi | Örneklerle Java Dize Yöntemleri