Gelişmiş Paylaşım: Java multithreading'i gerçekten anlıyor musunuz? Sizi bir kez çoklu iş parçacığı oynamaya götürür!

Metni nasıl tanıtacağımı bilmiyorum

Back-end öğrencilerin geliştirme sırasında az çok iş parçacıklı geliştirmeyi içereceğine inanıyorum.Java geliştirme olarak, sıklıkla çok iş parçacıklı geliştirme kullanıyorum.

Java dilinin multithreading ile başa çıkmada çok iyi olduğunu düşünüyorum. İş parçacıkları oluşturmak, başlatmak ve yönetmek için kısa kod kullanabiliriz.

Söyleyecek fazla bir şey yok, hadi aşağıdan ayrıntılı olarak bir göz atalım! ! !

Konular ve süreçler

Bu eski moda bir sorudur. Bir arka uç programcısı olarak, iş parçacıkları ve süreçler arasındaki farkı ayırt etmeliyiz. Hemen hemen tüm işletim sistemleri "süreç" kavramına sahiptir!

Bir görev, bir program, her çalışan program bir süreçtir!

Program çalışırken, birden çok sıralı yürütme akışı içerir ve her sıralı yürütme akışı bir iş parçacığıdır!

Süreç aşağıdaki üç özelliğe sahiptir:

  • Bağımsızlık: Bir süreç, sistemde kendi bağımsız kaynaklarına sahip olabilen bağımsız bir varlıktır ve her işlemin kendi özel adres alanı vardır. İşlemin kendisinin izni olmadan, kullanıcı diğer işlemlerin adres alanına doğrudan erişemez!
  • Dinamik: Bir işlem ile bir program arasındaki fark, bir programın yalnızca statik bir talimatlar kümesi olması, bir işlemin ise sistemde hareket eden bir dizi talimat olmasıdır. Sürece zaman kavramı da eklenir. Sürecin kendi yaşam döngüsü ve çeşitli durumları vardır, bu kavramlar programda mevcut değildir.
  • Eşzamanlılık: Tek bir işlemci üzerinde aynı anda birden fazla işlem yürütülebilir ve birden çok işlem birbirini etkilemez.

Çoklu iş parçacığı, çoklu süreç kavramını genişletir, böylece aynı süreç aynı anda birden fazla görevi yerine getirebilir. İş parçacığı, Hafif İşlem olarak da bilinir ve iş parçacığı, sürecin yürütme birimidir. Bir işlemin bir işletim sistemindeki konumu gibi, iş parçacıkları bağımsızdır ve bir programdaki eşzamanlı yürütme akışlarıdır. İşlem başlatıldığında, ana iş parçacığı oluşturulur. Çoğu uygulama için, genellikle yalnızca bir ana iş parçacığı gereklidir, ancak işlemde birden çok sıralı yürütme akışı da oluşturulabilir.Bu sıralı yürütme akışları evrelerdir ve her iş parçacığı birbirinden bağımsızdır.

Bir iş parçacığı, bir sürecin ayrılmaz bir parçasıdır. Bir işlemin birden çok iş parçacığı olabilir ve bir iş parçacığının bir üst süreç olması gerekir. Bir iş parçacığı kendi yığınına, kendi program sayacına ve kendi yerel değişkenlerine sahip olabilir, ancak kendi sistem kaynaklarına sahip değildir ve sürecin sahip olduğu tüm kaynakları üst sürecin diğer evreleriyle paylaşır.

En basit ifadeyle, bir program çalıştırıldıktan sonra en az bir işlem vardır Bir süreç birden fazla evre içerebilir, ancak en az bir evre dahil edilmelidir.

Çok iş parçacıklı okumanın faydaları

  • Bellek işlemler arasında paylaşılamaz, ancak iş parçacıkları arasında bellek paylaşımı çok kolaydır.
  • Sistem bir süreç oluşturduğunda, süreç için sistem kaynaklarını yeniden tahsis etmesi gerekir, ancak bir iş parçacığı oluşturmanın maliyeti çok daha azdır, bu nedenle çok görevli eşzamanlılık elde etmek için çoklu iş parçacığını kullanmak, çoklu işlemden daha verimlidir.
  • Java dili, temelde yatan işletim sisteminin zamanlama yöntemi olmaktan ziyade yerleşik çok parçacıklı işlev desteğine sahiptir, böylece Java'nın çok iş parçacıklı programlamasını basitleştirir.

Konu oluşturma yöntemi

Java, evreleri temsil etmek için Thread sınıfını kullanır ve tüm evre nesneleri, Thread sınıfının veya alt sınıflarının örnekleri olmalıdır.

Oluşturmak ve başlamak için Thread sınıfını devralın

  • Thread sınıfının bir alt sınıfını tanımlayın ve bu sınıfın run () yöntemini yazın. Run () yönteminin yöntem gövdesi, iş parçacığının tamamlanması gerektiğini temsil eder

Görev. Bu nedenle, run () yöntemine iş parçacığı yürütme gövdesi adı verilir.

  • Thread alt sınıfının bir örneğini oluşturun, yani bir thread nesnesi oluşturun.
  • İş parçacığını başlatmak için iş parçacığı nesnesinin start () yöntemini çağırın.
public class Main, İş Parçacığını genişletir { * * @param args * / public static void main (String args) { // İpliğin adını al System.out.println ("Konu adı:" + Thread.currentThread (). GetName ()); // Örneklemeden sonra başlamayı çağırın yeni Ana (). start (); // Örneklemeden sonra başlamayı çağırın yeni Ana (). start (); } / ** * İş Parçacığını devral ve çalıştırma yöntemini yeniden yaz * / @Override public void run () { System.out.println ("Konu başlangıcı! Konu adı:" + Thread.currentThread (). GetName ()); } }

Toplam üç iş parçacığı, bir ana iş parçacığı ve iki alt iş parçacığı çalıştırın!

Ana iş parçacığının yürütme gövdesi run () yöntemi tarafından değil, ana yöntem tarafından belirlenir Ana yöntemin yöntem gövdesi, ana iş parçacığının iş parçacığı yürütme gövdesini temsil eder.

  • Thread.currentThread (), yürütülen thread nesnesini döndüren Thread sınıfının statik bir metodudur!
  • getName (): Bu yöntem, Thread sınıfının bir örnek yöntemidir.Bu yöntem, yöntemi çağıran iş parçacığının adını döndürür.
  • setName (): İş parçacığının adını ayarlayın!

çok dikkat:

Thread sınıfı yöntemini miras alarak bir iş parçacığı sınıfı oluştururken, birden çok iş parçacığı, evre sınıfının örnek değişkenlerini paylaşamaz.

Runnable arabirimini uygulayın ve bir iş parçacığı sınıfı oluşturmak için run yöntemini geçersiz kılın

  • Runnable arabiriminin uygulama sınıfını tanımlayın ve arabirimin run0 yöntemini yeniden yazın Çalıştırmanın yöntem gövdesi (yöntem ayrıca iş parçacığının iş parçacığı yürütme gövdesidir;
  • Runnable uygulama sınıfının bir örneğini oluşturun ve bu örneği, gerçek iş parçacığı nesnesi olan bir Thread nesnesi oluşturmak için Thread hedefi olarak kullanın;
  • İş parçacığını başlatmak için start (iş parçacığı nesnesinin yöntemini çağırın.

Runnable nesnesi yalnızca Thread nesnesinin hedefi olarak işlev görür ve Runnable uygulama sınıfında bulunan run () yöntemi yalnızca iş parçacığı yürütme gövdesi olarak işlev görür. Gerçek iş parçacığı nesnesi hala bir Thread örneğidir, ancak Thread evre, hedefinin run () yöntemini yürütmekten sorumludur.

public class Main, Runnable { / ** * * @param args * / public static void main (String args) { // İpliğin adını al System.out.println ("Konu adı:" + Thread.currentThread (). GetName ()); Ana ana = yeni Ana (); yeni Konu (ana, "Birinci Konu"). start (); yeni Konu (ana, "Konu İKİ"). start (); } @Override public void run () { for (int i = 0; i < 100; i ++) { System.out.println (Thread.currentThread (). GetName ()); } } }

İki iş parçacığı başlatın ve her döngü için 100 kez İplik Adı yazdırın.

İş parçacığı oluşturmak için Çağrılabilir ve Geleceği kullanın

Java5'ten başlayarak Java, iş parçacığı yürütme gövdesi olarak kullanılabilen bir call () yöntemi sağlayan Callable arabirimini sağlar, ancak call () yöntemi run () yönteminden daha güçlüdür.

  • Call () yönteminin bir dönüş değeri olabilir;
  • Call () yöntemi, bir istisna atmayı bildirebilir.

Bu nedenle, Dişlinin hedefi olarak Çağrılabilir bir nesne sağlamak mümkündür ve evrenin evre yürütme gövdesi, Çağrılabilir nesnenin call () yöntemidir. Sorun şudur: Çağrılabilir arabirim, Java 5'te yeni bir arabirimdir ve Çalıştırılabilir arabirimin bir alt arabirimi değildir, bu nedenle Çağrılabilir nesne, İş Parçacığının hedefi olarak doğrudan kullanılamaz.

Java 5, Callable arabiriminde call () yönteminin dönüş değerini temsil etmek için Future arabirimini sağlar ve Future arabirimini uygulayan ve Runnable arabirimini uygulayan Future arabirimi için bir FutureTask uygulama sınıfı sağlar. Thread sınıfının hedefi olarak kullanılabilir. . Çağrılabilir arabirimin genel kısıtlamalara sahip olduğunu ve Çağrılabilir arabirimdeki genel parametre türünün, call () yönteminin dönüş değeri türüyle aynı olduğunu belirtmek gerekir. Ve Çağrılabilir arabirim işlevsel bir arabirim olduğundan, Lambda ifadelerini Çağrılabilir nesneler oluşturmak için kullanabilirsiniz.

Dönüş değeri olan bir iş parçacığı oluşturma ve başlatma adımları:

  • Callable arabiriminin bir uygulama sınıfını oluşturun ve call () yöntemini uygulayın. Call () yöntemi, iş parçacığı yürütme gövdesi olarak görev yapacak ve call () yöntemi bir dönüş değerine sahip olacak ve ardından Callable uygulama sınıfının bir örneğini oluşturacaktır. Java 8'den başlayarak, Çağrılabilir nesneler oluşturmak için doğrudan Lambda ifadelerini kullanabilirsiniz;
  • Callable nesnesinin call () yönteminin dönüş değerini kapsülleyen Callable nesnesini sarmak için FutureTask sınıfını kullanın;
  • Yeni bir iş parçacığı oluşturmak ve başlatmak için FutureTask nesnesini Thread nesnesinin hedefi olarak kullanın;
  • Alt iş parçacığının yürütülmesi sona erdikten sonra dönüş değerini elde etmek için FutureTask nesnesinin get () yöntemini çağırın.
  • java.util.concurrent.Callable dosyasını içe aktarın; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class Ana { / ** * * @param args * / public static void main (String args) { // İpliğin adını al System.out.println ("Konu adı:" + Thread.currentThread (). GetName ()); // Ana nesne oluştur Ana ana = yeni Ana (); // Çağrılabilir oluşturmak için Lambda ifadesini kullanın < Tamsayı > Nesne // FutureTask Callable'ı sarar < Tamsayı > Nesne FutureTask < Tamsayı > görev = yeni FutureTask < Tamsayı > (yeni Çağrılabilir < Tamsayı > () { @Override public Integer call (), Exception { int temp = 0; while (temp < 10) { temp ++; System.out.println ("Konu adı:" + Thread.currentThread (). GetName ()); } dönüş sıcaklığı; } }); // Konu başlat yeni Konu (görev) .start (); Deneyin { // Konu dönüş değeri Tamsayı tamsayı = task.get (); System.out.println ("İş parçacığı dönüşü:" + tamsayı); } catch (InterruptedException | ExecutionException e) { e.printStackTrace (); } } }

    İplik dizisi tamamlandığında, geri dönün!

    Yukarıdaki programda, Lambda ifadesi doğrudan Çağrılabilir nesneyi yaratmak için kullanılır, bu nedenle önce Çağrılabilir uygulama sınıfını ve ardından Çağrılabilir nesneyi yaratmaya gerek yoktur. Callable arabiriminin uygulanması ile Runnable arabiriminin uygulanması arasında çok fark yoktur, ancak Callable'ın call () yöntemi bildirimin bir istisna atmasına izin verir ve dönüş değerine izin verir.

    Konu oluşturmanın üç yolunun karşılaştırması

    Çok iş parçacıklı okuma, Thread sınıfını miras alarak veya Çalıştırılabilir ve Çağrılabilir arayüzleri uygulayarak elde edilebilir, ancak Çalıştırılabilir arayüz ve Çağrılabilir arayüzü uygulama yolu temelde aynıdır, ancak Çağrılabilir arayüzde tanımlanan metotların dönüş değerlerine sahip olması ve istisnaları bildirebilmesi dışında. Bu nedenle, Çalıştırılabilir arayüzün uygulanması ve Çağrılabilir arayüzün uygulanması aynı şekilde anlaşılabilir!

    Çalıştırılabilir ve Çağrılabilir arayüzleri uygulayarak çoklu okuma oluşturmanın avantajları ve dezavantajları:

    • İş parçacığı sınıfı yalnızca Çalıştırılabilir arabirimi veya Çağrılabilir arabirimi uygular ve ayrıca diğer sınıflardan da miras alabilir.
    • Bu şekilde, birden çok iş parçacığı aynı hedef nesneyi paylaşabilir, bu nedenle birden çok iş parçacığının aynı kaynağı işlemesi çok uygundur, böylece CPU, kod ve veriler daha iyi yansıtılan net bir model oluşturmak için ayrılabilir. Nesne yönelimli fikri.
    • Dezavantajı, programlamanın biraz daha karmaşık olmasıdır.Geçerli iş parçacığına erişmeniz gerekiyorsa, Thread.currentThread () yöntemini kullanmanız gerekir. Thread sınıfını miras alarak birden çok iş parçacığı oluşturmanın avantajları ve dezavantajları:
    • Dezavantajı, iş parçacığı sınıfının Thread sınıfını miras aldığından, artık diğer üst sınıflardan miras alamamasıdır.
    • Avantajı, yazmanın kolay olmasıdır.Geçerli iş parçacığına erişmeniz gerekiyorsa, Thread.currentThread () yöntemini kullanmanıza gerek yoktur.Geçerli iş parçacığını almak için bunu doğrudan kullanabilirsiniz.

    Bu nedenle, birden çok iş parçacığı oluşturmak için Çalıştırılabilir arabirimin ve Çağrılabilir arabirimin uygulanması önerilir.

    Süreç yaşam döngüsü

    İş parçacığı oluşturulup başlatıldığında, ne başlar başlamaz yürütme durumuna girmez, ne de her zaman yürütme durumunda değildir İş parçacığının yaşam döngüsünde, Yeni, Çalıştırılabilir ve Çalışıyor olması gerekir. , Engellendi ve Ölü (5 durum). Özellikle bir iş parçacığı başlatıldığında, CPU'nun tek başına çalışması için her zaman "meşgul" olamaz, bu nedenle CPU birden çok iş parçacığı arasında geçiş yapmak zorundadır, bu nedenle iş parçacığı durumu birden çok kez çalıştırma ve engelleme arasında geçiş yapacaktır.

    Yeni statü

    Yeni anahtar kelime ile oluşturulan iş parçacığı sonrasında, iş parçacığı şu anda yeni oluşturulmuş durumdadır! İş parçacığının yürütme gövdesi çalıştırılmayacak!

    Hazır durum

    İş parçacığı nesnesi start () yöntemini çağırdığında, iş parçacığı hazır durumdadır ve Java sanal makinesi bunun için bir yöntem çağrı yığını ve program sayacı oluşturur.Bu durumdaki iş parçacığı çalışmaya başlamaz, sadece iş parçacığının çalışabileceği anlamına gelir. İş parçacığının ne zaman çalışmaya başladığına gelince, bu, iş parçacığı zamanlayıcısının JVM'deki zamanlamasına bağlıdır.

    İşletim durumu

    İş parçacığı hazır durumundaysa ve CPU'yu elde etmişse, run () içindeki yöntem gövdesini yürütmeye başlar ve ardından iş parçacığı çalışma durumundadır!

    Engelleme durumu

    İş parçacığı çalışma durumunda olduğunda, iş parçacığı çalışırken kesilmesi gerekiyorsa, diğer iş parçacıkları yürütme şansı elde edebilir. Tüm modern masaüstü ve sunucu işletim sistemleri, önleyici zamanlama stratejileri kullanır, ancak cep telefonları gibi bazı küçük cihazlar, işbirliğine dayalı planlama stratejileri kullanabilir. Böyle bir sistemde, yalnızca bir iş parçacığı uyku () veya verim () Yöntem işgal edilen kaynaklardan vazgeçecektir - yani iş parçacığı aktif olarak işgal edilen kaynaklardan vazgeçmelidir.

    Aşağıdaki koşullar oluştuğunda, iş parçacığı bloke durumdadır:

    • İş parçacığı, kullanılan işlemci kaynaklarından aktif olarak vazgeçmek için sleep () yöntemini çağırır.
    • İş parçacığı, engelleyen bir GÇ yöntemini çağırır ve yöntem dönmeden önce iş parçacığı engellenir.
    • İş parçacığı bir eşitleme izleyicisi elde etmeye çalışıyor, ancak eşitleme izleyicisi başka bir iş parçacığı tarafından tutuluyor.
    • İş parçacığı bir bildirim bekliyor.
    • Program, iş parçacığını askıya almak için iş parçacığının suspend () yöntemini çağırır. Ancak bu yöntem kilitlenmeye eğilimlidir, bu nedenle mümkün olduğunca kaçınılmalıdır.

    Engellenen iş parçacığı, uygun olduğunda hazır durumuna yeniden girecektir.

    Aşağıdaki durumlarda, iş parçacığı tekrar gerekli durumda olacaktır:

    • Sleep () yöntemini çağıran iş parçacığı belirtilen süreyi geçti;
    • İş parçacığı tarafından çağrılan engelleyici GÇ yöntemi geri döndü;
    • İş parçacığı elde etmeye çalıştığı eşitleme izleyicisini başarıyla aldı;
    • İş parçacığı bir bildirim beklerken, diğer iş parçacığı bir bildirim yayınladı;
    • Askıya alınmış durumdaki iş parçacığı resume () yöntemi olarak adlandırılır.

    İplik ölümü

    İplik aşağıdaki üç şekilde bitecek ve bittikten sonra da ölecektir.

    • Run () veya call () yöntemi çalıştırılır ve iş parçacığı normal şekilde sona erer;
    • İş parçacığı yakalanmamış bir İstisna veya Hata atar;
    • İş parçacığını sonlandırmak için iş parçacığının stop () yöntemini doğrudan çağırın bu yöntem kolayca kilitlenmeye neden olabilir ve genellikle önerilmez.

    Yaşam döngüsü akış şeması aşağıdaki gibidir:

    İplik kontrolü

    Java'nın iş parçacığı desteği, iş parçacıklarının yürütülmesini denetlemek için bazı kullanışlı araçlar ve yöntemler sağlar.

    konuya katıl

    Thread, bir iş parçacığının başka bir iş parçacığının join () yöntemini tamamlamasını beklemesi için bir yöntem sağlar. Belirli bir program yürütme akışında başka bir iş parçacığının join () yöntemi çağrıldığında, çağrılan iş parçacığı join () yöntemi ile birleştirilen birleştirme iş parçacığı yürütülene kadar engellenir.

    Join () yöntemi genellikle büyük sorunu birçok küçük soruna bölmek için evreler kullanan programlar tarafından çağrılır ve her küçük soruna bir iş parçacığı atanır. Tüm küçük sorunlar çözüldüğünde, diğer işlemler için ana iş parçacığını arayın.

    public class JoinThread, Thread'ı genişletiyor {

    / ** * * @param args * / public static void main (String args) { new JoinThread ("threadA"). start (); for (int i = 0; i < 100; i ++) { eğer (i == 20) { JoinThread joinThread = new JoinThread ("konuya katıldı"); joinThread.start (); Deneyin { joinThread.join (); } catch (InterruptedException e) { e.printStackTrace (); } } } System.out.println ("Ana iş parçacığı"); } / ** * Ayar iş parçacığı adını oluştur * * @param threadName * / public JoinThread (String threadName) { süper (threadName); } @Override public void run () { System.out.println ("İş Parçacığı adı:" + getName ()); } }

    Yukarıdaki koddan ve çalışan sonuçlardan, en son yazdırılan ana iş parçacığının, ana iş parçacığının yürütülmeden önce "birleştirilmiş iş parçacığı" nın yürütülmesini beklemesi gerektiği anlamına geldiğini görmek zor değildir!

    Birleştirme iş parçacığını aşırı yüklemenin üç yolu vardır:

    • join (): Birleştirilen iş parçacığının yürütülmesinin tamamlanmasını bekleyin.
    • birleştirme (uzun milisaniye): İş parçacığının birleştirilmesini bekleyen en uzun süre
    • birleştirme (uzun milisaniye, int nanos): Bir iş parçacığının birleştirilmesini bekleyen en uzun süre milisaniye artı nanos nanosaniyedir.

    Arka plan ileti dizisi

    Arka plan iş parçacığı tam anlamıyla görülebilir Arka planda çalışan bir iş parçacığıdır Arka plan iş parçacığının bir özelliği vardır: tüm ön plan iplikleri ölürse, arka plan iş parçacığı otomatik olarak ölür. Belirtilen evreyi bir arka plan iş parçacığı olarak ayarlamak için, Thread nesnesinde setDaemon (true) yöntemini çağırabilirsiniz.

    Kod simülasyonu:

    public class ThreadTest, Thread { / ** * * @param args * / public static void main (String args) { ThreadTest threadTest = new ThreadTest (); // Arka plan dizisi olarak ayarla threadTest.setDaemon (true); threadTest.start (); for (int i = 0; i < 10; i ++) { System.out.println ("Ana iş parçacığı"); } } @Override public void run () { for (int i = 0; i < 100; i ++) { System.out.println ("thread run" + i); } } }

    Main - ana iş parçacığı yürütülmeyi bitirdiğinde, alt iş parçacığı da döngüye devam etmeden sona erer! Ayrıca Thread, belirtilen iş parçacığının bir arka plan iş parçacığı olup olmadığını belirlemek için isDaemon () yöntemini de sağlar!

    Ön plan iş parçacığının ölümünden sonra, JVM'nin ölümün arka plan iş parçacığını bildireceği, ancak yanıt vermek için talimatlar aldığı andan itibaren belirli bir süre alacağı unutulmamalıdır. Ve bir iş parçacığını arka plan iş parçacığı olarak ayarlamak için, iş parçacığı başlatılmadan önce ayarlanmalıdır, yani start () yönteminden önce setDaemon (true) çağrılmalıdır, aksi takdirde IllegalThreadStateException atılır.

    Konu uykusu

    Bunun anlaşılması kolaydır, yani iş parçacığının askıya alınmış duruma geçmesine izin verin Thread, iş parçacığının ne kadar süre uyuyacağını milisaniye cinsinden belirleyebilen sleep () yöntemini sağlar.

    public class ThreadTest, Thread { / ** * * @param args * / public static void main (String args) { //IP oluşurmak ThreadTest threadTest = new ThreadTest (); // İlçeyi başlat threadTest.start (); } @Override public void run () { for (int i = 0; i < 10; i ++) { System.out.println ("thread run" + i); Deneyin { // Bir saniye uyuduktan sonra sırayla çalışmaya devam edin Thread.sleep (1000); } catch (InterruptedException e) { e.printStackTrace (); } } } }

    Konu imtiyazı yieId

    Verim () yöntemi, sleep () yöntemine benzer bir yöntemdir.Ayrıca, Thread sınıfı tarafından sağlanan statik bir yöntemdir.Ayrıca, şu anda yürütülen iş parçacığını askıya alabilir, ancak iş parçacığını engellemez, yalnızca iş parçacığını değiştirir Hazır durumuna gidin. Yield () sadece mevcut iş parçacığını duraklatır ve sistemin iş parçacığı zamanlayıcısının onu yeniden planlamasını sağlar.Bir iş parçacığı askıya almak için getiri () yöntemini çağırdığında, iş parçacığı zamanlayıcının onu yeniden yürütmek üzere programlaması tamamen mümkündür.

    Vaka kodu

    public class ThreadTest, Thread { / ** * * @param args * / public static void main (String args) { ThreadTest threadTest1 = new ThreadTest ("Thread ONE"); threadTest1.start (); ThreadTest threadTest2 = new ThreadTest ("Konu İKİ"); threadTest2.start (); } public ThreadTest (String threadName) { süper (threadName); } @Override public void run () { for (int i = 0; i < 100; i ++) { System.out.println (getName () + i); // i 20'ye eşit olduğunda, iş parçacığı tavizleri verin eğer (i == 20) { Thread.yield (); } } } }

    İş parçacığı önceliği kontrolü

    Thread sınıfı, belirtilen iş parçacığının önceliğini ayarlamak ve döndürmek için setPriority (int newPriority) ve getPriority () yöntemleri sağlar. SetPriority () yönteminin parametresi 1 ile 10 arasında değişen bir tam sayı olabilir veya Thread sınıfını kullanabilirsiniz Aşağıdaki üç statik sabit.

    public class ThreadTest, Thread { / ** * * @param args * / public static void main (String args) { ThreadTest threadTest1 = new ThreadTest ("Yüksek Öncelik"); // Önceliği ayarla threadTest1.setPriority (MAX_PRIORITY); threadTest1.start (); ThreadTest threadTest2 = new ThreadTest ("düşük öncelikli"); // Önceliği ayarla threadTest1.setPriority (NORM_PRIORITY); threadTest2.start (); } public ThreadTest (String threadName) { süper (threadName); } @Override public void run () { for (int i = 0; i < 100; i ++) { System.out.println (getName () + i); // i 20'ye eşit olduğunda, iş parçacığı tavizleri verin eğer (i == 20) { Thread.yield (); } } } }
    • MAX PRIORITY: değeri 10'dur;
    • MIN PRIORITY: değeri 1'dir;
    • NORM PRIORITY: Değeri 5'tir.

    Kısacası, setPriority değeri ne kadar yüksekse, iş parçacığı o kadar fazla yürütme fırsatı elde eder! Değeri doğrudan ayarlamanız önerilmez

    , Windows 2000 yalnızca 7 öncelik düzeyi sağlar. Bu nedenle, iş parçacığı için önceliği doğrudan belirlemekten kaçınmaya çalışmalısınız ve programın en iyi taşınabilirliğini sağlamak için önceliği ayarlamak için MAX_ PRIORITY, MIN_ PRIORITY ve NORM PRIORITY üç statik sabiti kullanmalısınız.

    İş parçacığı senkronizasyonunun "gerekliliği"

    Şimdi bir senaryo varsayalım. 12306 Bahar Festivali tatil sezonunda, bilet satın almak için çok fazla baskı olduğunu hepimiz biliyoruz. Bilet satan birden çok pencere var, bu da çoklu okumaya eşdeğerdir. Şimdi bu durumla karşılaşıp karşılaşmayacağınızı düşünün. Hala satılmamış son bilet varsa ve şimdi aynı anda sorgulamak için iki pencere varsa, her ikisi de son bilet olarak görüntülenir, bu da bu bileti satmak için her iki pencerenin de çalıştırılabileceği anlamına gelir ve işlem başarılı olduğunda Bundan sonra, arka uç veritabanındaki bir bilet iki kişilik ve son olarak -1'e satılır. Ya da belki aynı anda biletleri kontrol edip satıyor ve satılan miktar kalan miktara uymuyor! Elbette 12306'da bu sorun olmaz, biz sadece hayal gücüne bir örnek verelim! Bu algoritma problemini çözmek için!

    Şimdi bilet satış algoritmasını yazalım!

    Kalan bilet sayısını tanımlayın (12306 bilet veritabanını simüle ederek)

    public class TiketAdmin { özel int tiketNum; public TiketAdmin (int tiketNum) { this.tiketNum = tiketNum; } public int getTiketNum () { dönüş tiketNum; } public void setTiketNum (int tiketNum) { this.tiketNum = tiketNum; } }

    Biletlemeyi başlatın, biletleri almak için iki pencereyi simüle edin

    / ** * / public class ThreadTest, Thread { //Toplam oy özel statik TiketAdmin tiketAdmin; public static void main (String args) { // Kalan oyları ilklendirin tiketAdmin = yeni TiketAdmin (100); ThreadTest threadTest1 = new ThreadTest ("Pencere 1"); threadTest1.start (); ThreadTest threadTest2 = new ThreadTest ("Pencere 2"); threadTest2.start (); } public ThreadTest (String threadName) { süper (threadName); } @Override public void run () { if (tiketAdmin.getTiketNum () > 0) { Deneyin { uyku (300 + tiketAdmin.getTiketNum ()); } catch (InterruptedException e) { e.printStackTrace (); } // Oy sayısı 0'dan büyükse bir bilet sat if (tiketAdmin.getTiketNum () > 0) { // Bilet satışlarını simüle edin tiketAdmin.setTiketNum (tiketAdmin.getTiketNum () - 1); // Kalan oyları yazdırın System.out.println (getName () + "Bir tane satıldı, kalan oylar:" + tiketAdmin.getTiketNum ()); } } } }

    İki iş parçacığı (iki pencere), aynı anda bilet sat, 99 bilet kaldı! Ancak bu iki bilet sattı ve bu kalan toplam oy sayısına karşılık gelmiyor. Böyle bir konu güvenli değil!

    Bu mantığın büyük bir sorunu olduğunu ve oy sayısının doğru olmadığını gördük! Nasıl çözülür, böylece iş parçacığı senkronizasyonu gerçekleştirilmelidir!

    Bu sorunu çözmek için, Java'nın çoklu okuma desteği, bu sorunu çözmek için bir senkronizasyon izleyicisi sunar Bir senkronizasyon izleyicisi kullanmanın genel yöntemi, kod bloklarını senkronize etmektir.

    senkronize iş parçacığı senkronizasyonu

    Şimdi senkronize edilmiş ekle

    / ** * * * / public class ThreadTest, Thread { //Toplam oy özel statik TiketAdmin tiketAdmin; public static void main (String args) { // Kalan oyları ilklendirin tiketAdmin = yeni TiketAdmin (100); ThreadTest threadTest1 = new ThreadTest ("Pencere 1"); threadTest1.start (); ThreadTest threadTest2 = new ThreadTest ("Pencere 2"); threadTest2.start (); } public ThreadTest (String threadName) { süper (threadName); } @Override public void run () { senkronize (tiketAdmin) { if (tiketAdmin.getTiketNum () > 0) { Deneyin { uyku (300 + tiketAdmin.getTiketNum ()); } catch (InterruptedException e) { e.printStackTrace (); } // Oy sayısı 0'dan büyükse bir bilet sat if (tiketAdmin.getTiketNum () > 0) { // Bilet satışlarını simüle edin tiketAdmin.setTiketNum (tiketAdmin.getTiketNum () - 1); // Kalan oyları yazdırın System.out.println (getName () + "Bir tane satıldı, kalan oylar:" + tiketAdmin.getTiketNum ()); } } } } }

    Şimdi veriler doğru! Aşağıda senkronize edilmiş olarak açıklayın!

    Senkronize kod bloğuna karşılık gelen, Java'nın çok iş parçacıklı güvenlik desteği ayrıca senkronize bir yöntem sağlar Senkronize yöntem, senkronize edilmiş anahtar sözcükle bir yöntemi değiştirmektir ve bu yöntem, senkronize bir yöntem olarak adlandırılır. Eşitlenmiş değiştirilmiş örnek yöntemleri (statik olmayan yöntemler) için, bir eşitleme izleyicisini açıkça belirtmeye gerek yoktur Senkronize bir yöntemin eşitleme izleyicisi, yöntemi çağıran nesne olan budur.

    İplik güvenliği aşağıdaki özelliklere sahiptir:

    • Bu tür nesnelere birden çok iş parçacığı tarafından güvenli bir şekilde erişilebilir;
    • Her iş parçacığı, nesnenin herhangi bir yöntemini çağırdıktan sonra doğru sonucu alacaktır;
    • Her iş parçacığı nesnenin herhangi bir yöntemini çağırdıktan sonra, nesnenin durumu makul bir durumda kalır. .

    Senkronizasyon izleme kilidini serbest bırakın

    Herhangi bir iş parçacığı, senkronizasyon kodu bloğuna ve senkronizasyon yöntemine girmeden önce, önce senkronizasyon monitöründeki kilidi almalıdır.Senkronizasyon monitöründeki kilit ne zaman serbest bırakılacak? Program, senkronizasyon monitöründeki kilidi açıkça bırakamaz. İş parçacığı aşağıdaki gibi olacaktır. Birkaç durumda senkronizasyon monitörünün kilidini açın.

    • Senkronizasyon yönteminin yürütülmesi ve mevcut iş parçacığının senkronizasyon kodu bloğu sona erdikten sonra, mevcut iş parçacığı, senkronizasyon izleyicisini serbest bırakır.
    • Mevcut iş parçacığı, senkronize edilmiş bir kod bloğunda ve senkronize bir yöntemde bir kesme veya dönüşle karşılaştığında, kod bloğu sonlandırılır ve yöntem yürütülmeye devam eder, mevcut iş parçacığı senkronizasyon izleyicisini serbest bırakır.
    • Geçerli iş parçacığı, eşitleme kodu bloğu veya eşitleme yönteminde işlenmemiş bir Hata veya İstisna içerdiğinde, bu kod bloğunun veya yöntemin anormal şekilde sona ermesine neden olur, mevcut iş parçacığı senkronizasyon izleyicisini serbest bırakır.
    • Mevcut iş parçacığı, senkronizasyon kodu bloğunu veya senkronizasyon yöntemini yürüttüğünde ve program, senkronizasyon izleme nesnesinin wait () yöntemini yürüttüğünde, mevcut iş parçacığı askıya alınır ve senkronizasyon izleyicisi serbest bırakılır. Aşağıda gösterilen durumda, iş parçacığı senkronizasyon monitörünü serbest bırakmayacaktır.
    • Bir iş parçacığı bir eşitleme kodu bloğu veya bir eşitleme yöntemi yürüttüğünde, program, geçerli iş parçacığının yürütülmesini askıya almak için Thread.sleep () ve Thread.yield () yöntemlerini çağırır ve mevcut iş parçacığı, senkronizasyon monitörünü serbest bırakmaz.
    • İş parçacığı senkronizasyon kodu bloğunu yürüttüğünde, diğer evreler iş parçacığını askıya almak için iş parçacığının suspend () yöntemini çağırır ve iş parçacığı senkronizasyon izleyicisini bırakmaz. Elbette, program iş parçacıklarını kontrol etmek için suspend () ve resume () yöntemlerini kullanmaktan kaçınmalıdır.

    Kilit nesnesi, iş parçacığı senkronizasyonunu kontrol eder

    import java.util.concurrent.locks.ReentrantLock; public class TiketAdmin { özel nihai ReentrantLock reentrantLock = new ReentrantLock (); / ** * Bilet satış operasyonu * / public void sellTiket () { reentrantLock.lock (); Deneyin { // İş parçacığı için güvenli kod sağlayın } en sonunda { reentrantLock.unlock (); } } }

    Lock, senkronize yöntemlere ve senkronize kod bloklarına göre daha geniş bir kilitleme işlemi yelpazesi sağlar Kilit, daha esnek bir yapıya izin verir, çok farklı özelliklere sahip olabilir ve birden çok ilgili Koşul nesnesini destekler.

    Java 8, çoğu senaryoda geleneksel ReentrantReadWriteLock'un yerini alabilecek yeni bir StampedLock sınıfı türü ekler.ReentrantReadWriteLock, okuma ve yazma işlemleri için üç kilit modu sağlar: Yazma, OkumaOptimistik ve Okuma.

    Kilitlenme

    Kilitlenme böyledir, iki iş parçacığı birbirinin kilidi açmasını beklerken kilitlenme olur! Thread sınıfının suspend () yöntemi kolayca kilitlenmeye yol açabileceğinden, Java artık bu yöntemin İş parçacığının yürütülmesini askıya alın. Bu nedenle, çok iş parçacıklı programlamada kilitlenmeleri önlemek için önlemler alınmalıdır.

    Konu iletişimi

    Nesne, iş parçacığı iletişimini gerçekleştirir

    Bu işlevi gerçekleştirmek için, Object sınıfı tarafından sağlanan wait (), notify () ve notifyAll () yöntemlerini kullanabilirsiniz.Bu üç yöntem Thread sınıfına ait değildir, ancak Object sınıfına aittir. Ancak bu üç yöntem, aşağıdaki iki duruma bölünebilen senkronizasyon izleme nesnesi tarafından çağrılmalıdır:

    • Senkronize ile dekore edilmiş senkronize yöntem için, sınıfın varsayılan örneği (bu) senkronize monitör olduğundan, bu üç metot doğrudan senkronize metotta çağrılabilir.
    • Senkronize ile dekore edilmiş senkronize kod bloğu için senkronize edilmiş monitör, senkronize edildikten sonra parantez içindeki nesnedir, bu nedenle bu üç yöntemi çağırmak için bu nesneyi kullanmanız gerekir.

    Üç wait () yöntemi ile ilgili olarak, notify () ve notifyAll (), ilgili açıklamalar:

    • wait (): Mevcut iş parçacığının, iş parçacığını uyandırmak için senkronizasyon izleyicisinin notify () yöntemini veya notifyAll () yöntemini çağırana kadar beklemesine neden olur. Wait () yönteminin üç biçimi vardır: zaman parametresi olmadan bekleyin, diğer iş parçacıkları bildirene kadar bekleyin), milisaniye parametresiyle bekleyin () ve milisaniye ve nanosaniye parametreleriyle bekleyin () Bu iki yöntem belirtilmeyi bekliyor. Zaman geçtikten sonra otomatik olarak uyan. Wait () yöntemini çağıran mevcut iş parçacığı, senkronizasyon monitöründeki kilidi serbest bırakır.
    • notify (): bu senkronizasyon monitöründe bekleyen tek bir iş parçacığı uyandırır. Bu senkronizasyon monitöründe tüm iş parçacıkları bekliyorsa, bunlardan biri uyandırılacaktır. Seçim keyfi. Sadece mevcut iş parçacığı senkronizasyon monitöründeki kilidi bıraktıktan ve wait () yöntemini kullandıktan sonra, uyandırılmış iş parçacığı yürütülebilir.
    • notifyAll (): bu senkronizasyon monitöründe bekleyen tüm iş parçacıklarını uyandırın. Sadece mevcut iş parçacığı senkronizasyon monitöründeki kilidi bıraktıktan sonra, uyandırılmış iş parçacığı yürütülebilir

    Çağrılabilir iş parçacığı oluştur

    Koşul sınıfı aşağıdaki üç yöntemi sağlar:

    • await (): Örtük senkronizasyon izleyicisindeki wait () yöntemine benzer şekilde, mevcut iş parçacığının diğer iş parçacıklarının iş parçacığı uyandırmak için Koşulun signal () yöntemini veya signalAll () yöntemini çağırmasına kadar beklemesine neden olur. Await () yönteminin, daha bol bekleme işlemlerini tamamlayabilen long awaitNanos (longnanosTimeout), void awaitUninterruptouslyOawaitUntil (Date deadline) gibi daha fazla çeşidi vardır.
    • signal (): bu Kilit nesnesinde bekleyen tek bir iş parçacığı uyandırır. Tüm iş parçacıkları Kilit nesnesini bekliyorsa, bunlardan biri uyandırılacaktır. Seçim keyfi. Sadece mevcut iş parçacığı Lock nesnesindeki kilidi terk ettiğinde ve await () kullandığında, uyandırılmış evre çalıştırılabilir.
    • signalAll (): Bu Kilit nesnesini bekleyen tüm iş parçacıklarını uyandır. Sadece mevcut iş parçacığı Kilit nesnesi üzerindeki kilidi bıraktıktan sonra, uyandırılmış iş parçacığı çalıştırılabilir.

    Konu Havuzu

    Sistemin yeni bir iş parçacığı başlatma maliyeti nispeten yüksektir, çünkü işletim sistemiyle etkileşimi içerir. Bu durumda, iş parçacığı havuzunu kullanmak performansı artırabilir, özellikle programda kısa ömürlü çok sayıda iş parçacığı oluşturulması gerektiğinde, iş parçacığı havuzunu kullanmayı düşünmelisiniz. Veritabanı bağlantı havuzuna benzer şekilde, iş parçacığı havuzu, sistem başladığında çok sayıda boşta iş parçacığı oluşturur.Program, iş parçacığı havuzuna bir Çalıştırılabilir nesne veya Çağrılabilir nesne iletir ve iş parçacığı havuzu, çalıştırma () veya çağrılarını yürütmek için bir iş parçacığı başlatır. () yöntemi, run () veya call () yöntemi sona erdiğinde, iş parçacığı ölmez, ancak iş parçacığı havuzuna geri dönerek, bir sonraki Runnable nesnesinin run () veya call () yöntemini yürütmeyi beklerken tekrar boşta kalır. .

    Java 5, iş parçacığı havuzları oluşturmak için yeni bir Yürütücü fabrika sınıfı ekledi.Fabrika sınıfı iş parçacığı havuzları oluşturmak için aşağıdaki statik fabrika yöntemlerini içerir:

    • newCachedThreadPool): Önbelleğe alma işlevi ile bir iş parçacığı havuzu oluşturun, sistem gerektiği gibi iş parçacıkları oluşturur ve bu iş parçacıkları iş parçacığı havuzunda önbelleğe alınacaktır;
    • newFixedThreadPool (int nThreads); Sabit sayıda iş parçacığı ile yeniden kullanılabilir bir iş parçacığı havuzu oluşturun.
    • newSingleThreadExecutor (): 1 parametresiyle newFixedThread Pool () yöntemini çağırmaya eşdeğer olan tek iş parçacıklı bir iş parçacığı havuzu oluşturun;
    • newScheduledThreadPool (int corePoolSize): Belirli bir süre sonra iş parçacığı görevlerini yürütebilen, belirtilen sayıda iş parçacığı içeren bir iş parçacığı havuzu oluşturun. corePoolSize, havuzda kaydedilen iş parçacığı sayısını ifade eder, iş parçacıkları boşta olsalar bile, bunlar iş parçacığı havuzuna da kaydedilir;
    • newSingleThreadScheduledExecutor (): Belirli bir gecikmeden sonra iş parçacığı görevlerini yürütebilen yalnızca bir iş parçacığı içeren bir iş parçacığı havuzu oluşturun;
    • ExecutorService new WorkStealingPool (int paralellik): Belirli bir paralellik düzeyini desteklemek için yeterli iş parçacığı tutan bir iş parçacığı havuzu oluşturun Bu yöntem aynı zamanda rekabeti azaltmak için birden fazla kuyruk kullanır;
    • ExecutorService new WorkStealingPool (): Bu yöntem, önceki yöntemin basitleştirilmiş bir sürümüdür. Mevcut makinenin 4 CPU'su varsa, hedef paralellik seviyesi 4'e ayarlanır, bu da önceki yönteme parametre olarak 4'ü geçmeye eşdeğerdir.

    İş parçacığı görevlerini gerçekleştirmek için iş parçacığı havuzunu kullanma adımları aşağıdaki gibidir:

    • Bir iş parçacığı havuzunu temsil eden bir ExecutorService nesnesi oluşturmak için Executors sınıfının statik fabrika yöntemini çağırın;
    • Görevleri gerçekleştirmek için bir iş parçacığı olarak Runnable uygulama sınıfı veya Çağrılabilir uygulama sınıfının bir örneğini oluşturun;
    • Runnable örneğini veya Çağrılabilir örneği göndermek için ExecutorService nesnesinin submit () yöntemini çağırın;
    • Herhangi bir görev göndermek istemediğinizde, iş parçacığı havuzunu kapatmak için ExecutorService nesnesinin shutdown () yöntemini çağırın.

    Kod demosu:

    import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; / ** * * * / public class ThreadPack { public static void main (String args) { // Sabit konu sayısı ExecutorService executorService = Executors.newFixedThreadPool (10); // Lambda iş parçacığı oluşturur Runnable runnable = () - > { for (int i = 0; i < 100; i ++) { System.out.println ("Konu:" + Thread.currentThread (). GetName ()); } }; // İki iş parçacığı havuzuna gönder executorService.submit (çalıştırılabilir); executorService.submit (çalıştırılabilir); // İş parçacığı havuzunu kapat executorService.shutdown (); } } Gördüğünüz gibi, alternatif uygulama

    Gördüğünüz gibi, alternatif uygulama

    Yukarıdaki programda oluşturulmuştur Runnable Uygulama sınıfı ile başlangıçta iş parçacığı havuzunun oluşturulması arasında çok fazla fark yoktur. Runnable Sınıfın uygulanmasından sonra, program doğrudan evreler oluşturmaz ve Runnable Görev, ancak görevi gerçekleştirmek için iş parçacığı havuzu aracılığıyla!

    Tamam, bugün sizinle paylaşacağım. İlgileniyorsanız, beğenmeyi ve dikkat etmeyi unutmayın!

    Programcı: Son zamanlarda iş parçacığı güvenliği ve performans arasındaki dengeyi düşünüyordum
    önceki
    İş arkadaşları eşzamanlılığın zor olduğunu söylüyor, Java eşzamanlılığı da öyle mi? Eşzamanlılık mekanizmasının altında yatan üç ilke
    Sonraki
    Programcı: Çok iş parçacıklı paylaşılan kaynak senkronizasyonu, dikkatli bakmazsanız pişman olacaksınız
    Programcı: İş parçacığını nasıl doğru bir şekilde durduracağınızı hala bilmiyor musunuz?
    İyi makale paylaşımı: JavaWeb'de size yönetici oturum açma bilgilerini ve CURD bilgilerini göstermek için 5 resim
    Programcı: Nesnenin özünü ortaya çıkarmak için Java'nın 5 özelliği
    Çin'in bilim ve teknolojisinin bir başka güzel haberi daha var, kuantum çip teknolojik sınırı aşıyor ve dünya hemen köşede!
    Maskeyi litografi makinesi ile değiştirin! Hollanda kontrolden çıktı ve yardım istiyor, Rusya: Çin'in büyük bir fırsatı var
    Uyarılmış! Çin bağımsız olarak gelecekte artık kısıtlanmayacak bir "kuantum çipi" geliştirdi
    14nm işleminin seri üretiminin ardından, Çin'in çipi 6nm'lik buz darboğazını kırarak başka bir iyi habere sahip
    inanılmaz! Çok az yerli litografi makinesi üreticisi var ve bu görünmez dev bize umut veriyor.
    Çin Bilimler Akademisi başka bir büyük atılım yaptı! Batılı ülkeler buna inanamıyor, Çin bunu nasıl yaptı?
    Du Yuesheng, karısının "raydan çıktığını" ve idarenin basit ve zorba olduğunu ve bir Şangay kahramanı olmayı hak ettiğini buldu.
    Programcı: Altta yatan bilgisayar analiziyle birleştirilmiş "İplik Güvenliği İlkelerinin Analizi"
    To Top