JavaRush /Java Blogu /Random-AZ /Java-nı iplə məhv edə bilməzsiniz: II hissə - sinxronizas...
Viacheslav
Səviyyə

Java-nı iplə məhv edə bilməzsiniz: II hissə - sinxronizasiya

Qrupda dərc edilmişdir

Giriş

Beləliklə, bilirik ki, Java-da mövzular var, onlar haqqında " Bir Mövzu ilə Java-nı korlaya bilməzsiniz: I hissə - Mövzular " icmalında oxuya bilərsiniz . Eyni zamanda iş görmək üçün iplər lazımdır. Buna görə də, çox güman ki, iplər bir şəkildə bir-biri ilə qarşılıqlı əlaqədə olacaqlar. Bunun necə baş verdiyini və hansı əsas nəzarətlərə malik olduğumuzu anlayaq. Java-nı iplə məhv edə bilməzsiniz: II hissə - sinxronizasiya - 1

Məhsuldarlıq

Thread.yield() metodu sirli və nadir hallarda istifadə olunur. İnternetdə onun təsvirinin bir çox variantı var. O qədər ki, bəziləri öz prioritetlərini nəzərə alaraq ipin aşağıya doğru hərəkət edəcəyi bir növ növbə haqqında yazırlar. Kimsə yazır ki, mövzu öz statusunu çalışandan işlək vəziyyətə dəyişəcək (baxmayaraq ki, bu statuslara bölmə yoxdur və Java onları ayırmır). Amma reallıqda hər şey çox daha naməlum və müəyyən mənada daha sadədir. Java-nı iplə məhv edə bilməzsiniz: II hissə - sinxronizasiya - 2Metod sənədləri mövzusunda " JDK-6416721: (spec thread) Fix Thread.yield() javadocyield " səhvi var . Əgər onu oxusanız, aydın olur ki, əslində metod yalnız Java mövzu planlayıcısına bu mövzuya daha az icra müddəti verilə biləcəyi barədə bəzi tövsiyələr verir. Ancaq əslində nə baş verəcək, planlaşdırıcının tövsiyəni eşidib-eşitməyəcəyi və ümumiyyətlə nə edəcəyi JVM və əməliyyat sisteminin tətbiqindən asılıdır. Və ya bəlkə də bəzi digər amillərdən. Bütün çaşqınlıqlar çox güman ki, Java dilinin inkişafı zamanı multithreading-in yenidən nəzərdən keçirilməsi ilə bağlı idi. Daha ətraflı " Java Thread.yield()-a Qısa Giriş " icmalında oxuya bilərsiniz . yield

Yuxu - yuxuya gedən ip

Bir iplik icrası zamanı yuxuya gedə bilər. Bu, digər mövzularla qarşılıqlı əlaqənin ən sadə növüdür. Java kodunun icra olunduğu Java virtual maşınının quraşdırıldığı əməliyyat sistemində Thread Scheduler adlı öz mövzu planlayıcısı var. Hansı ipin nə vaxt keçəcəyinə qərar verən odur. Proqramçı bu planlaşdırıcı ilə birbaşa Java kodundan əlaqə saxlaya bilməz, lakin o, JVM vasitəsilə planlaşdırıcıdan mövzunu bir müddət dayandırmağı və onu “yuxu rejiminə keçirməyi” istəyə bilər. Daha ətraflı " Thread.sleep() " və " Multithreading necə işləyir " məqalələrində oxuya bilərsiniz . Bundan əlavə, siz iplərin Windows OS-də necə işlədiyini öyrənə bilərsiniz: " Windows Thread-in daxili hissələri ". İndi biz bunu öz gözlərimizlə görəcəyik. Aşağıdakı kodu faylda saxlayaq HelloWorldApp.java:
class HelloWorldApp {
    public static void main(String []args) {
        Runnable task = () -> {
            try {
                int secToWait = 1000 * 60;
                Thread.currentThread().sleep(secToWait);
                System.out.println("Waked up");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        Thread thread = new Thread(task);
        thread.start();
    }
}
Gördüyünüz kimi, 60 saniyə gözləyən bir vəzifəmiz var, bundan sonra proqram başa çatır. Biz tərtib edirik javac HelloWorldApp.javavə işlədirik java HelloWorldApp. Ayrı bir pəncərədə işə salmaq daha yaxşıdır. Məsələn, Windows-da bu belə olardı: start java HelloWorldApp. jps əmrindən istifadə edərək prosesin PID-ni tapırıq və istifadə edərək mövzuların siyahısını açırıq jvisualvm --openpid pidПроцесса: Java-nı iplə məhv edə bilməzsiniz: II hissə - sinxronizasiya - 3Gördüyünüz kimi mövzumuz Sleeping statusuna daxil olub. Əslində, cari ipi yatmaq daha gözəl edilə bilər:
try {
	TimeUnit.SECONDS.sleep(60);
	System.out.println("Waked up");
} catch (InterruptedException e) {
	e.printStackTrace();
}
Yəqin ki, hər yerdə emal etdiyimizi görmüsünüz InterruptedException? Səbəbini anlayaq.

Mövzunun kəsilməsi və ya Thread.interrupt

Məsələ burasındadır ki, ip yuxuda gözləyərkən kimsə bu gözləməni dayandırmaq istəyə bilər. Bu vəziyyətdə belə bir istisna ilə məşğul oluruq. Thread.stopBu, metod köhnəlmiş elan edildikdən sonra edildi , yəni. köhnəlmiş və istifadəsi arzuolunmazdır. Bunun səbəbi, metod çağırılanda stopipin sadəcə "öldürülməsi" idi ki, bu da çox gözlənilməz idi. Axının nə vaxt dayandırılacağını bilə bilmədik, məlumatların ardıcıllığına zəmanət verə bilmədik. Təsəvvür edin ki, siz fayla məlumat yazırsınız və sonra axın məhv olur. Ona görə də onlar axını öldürməmək, onun kəsilməli olduğunu bildirmək daha məntiqli olduğuna qərar verdilər. Buna necə reaksiya vermək isə axının özündən asılıdır. Ətraflı təfərrüatları Oracle-ın " Thread.stop niyə köhnəlmişdir? " Bir misala baxaq:
public static void main(String []args) {
	Runnable task = () -> {
		try {
			TimeUnit.SECONDS.sleep(60);
		} catch (InterruptedException e) {
			System.out.println("Interrupted");
		}
	};
	Thread thread = new Thread(task);
	thread.start();
	thread.interrupt();
}
Bu misalda biz 60 saniyə gözləməyəcəyik, lakin dərhal 'Kəsilən' yazısını çap edəcəyik. Bunun səbəbi mövzunun metodunu adlandırdığımızdır interrupt. Bu üsul "kesinti statusu adlı daxili bayraq" təyin edir. Yəni, hər bir mövzu birbaşa əlçatan olmayan daxili bayraqlara malikdir. Ancaq bu bayraqla qarşılıqlı əlaqədə olmaq üçün yerli üsullarımız var. Ancaq bu yeganə yol deyil. Bir ip icra prosesində ola bilər, bir şey gözləmir, sadəcə hərəkətləri yerinə yetirir. Ancaq işinin müəyyən bir nöqtəsində onu tamamlamaq istəyəcəklərini təmin edə bilər. Misal üçün:
public static void main(String []args) {
	Runnable task = () -> {
		while(!Thread.currentThread().isInterrupted()) {
			//Do some work
		}
		System.out.println("Finished");
	};
	Thread thread = new Thread(task);
	thread.start();
	thread.interrupt();
}
Yuxarıdakı nümunədə whileipin xaricdən kəsilməsinə qədər döngənin işləyəcəyini görə bilərsiniz. isInterrupted bayrağı haqqında bilmək üçün vacib olan şey odur ki, onu tutsaq InterruptedException, bayraq isInterruptedsıfırlanır və sonra isInterruptedo, false qaytaracaq. Thread sinfində yalnız cari mövzuya aid olan statik metod da var - Thread.interrupted() , lakin bu üsul bayrağı false vəziyyətinə qaytarır! Daha ətraflı " Mövzunun kəsilməsi " bölməsində oxuya bilərsiniz .

Qoşulun — Başqa mövzunun tamamlanmasını gözləyirik

Ən sadə gözləmə növü başqa bir başlığın tamamlanmasını gözləməkdir.
public static void main(String []args) throws InterruptedException {
	Runnable task = () -> {
		try {
			TimeUnit.SECONDS.sleep(5);
		} catch (InterruptedException e) {
			System.out.println("Interrupted");
		}
	};
	Thread thread = new Thread(task);
	thread.start();
	thread.join();
	System.out.println("Finished");
}
Bu nümunədə yeni ip 5 saniyə yatacaq. Eyni zamanda, əsas iplik yuxu ipi oyanana və işini bitirənə qədər gözləyəcəkdir. JVisualVM vasitəsilə baxsanız, mövzunun vəziyyəti belə görünəcək: Java-nı iplə məhv edə bilməzsiniz: II hissə - sinxronizasiya - 4Monitorinq alətləri sayəsində iplə nə baş verdiyini görə bilərsiniz. Metod olduqca sadədir, çünki o, sadəcə olaraq çağırıldığı mövzu canlı olduğu halda joinicra edilən java kodu ilə bir üsuldur . waitİp öləndən sonra (xitam verildikdə), gözləməyə son qoyulur. Metodun bütün sehri budur join. Ona görə də keçək ən maraqlı hissəyə.

Konsepsiya Monitoru

Multithreading-də Monitor kimi bir şey var. Ümumiyyətlə, monitor sözü latın dilindən “nəzarətçi” və ya “nəzarətçi” kimi tərcümə olunur. Bu məqalə çərçivəsində mahiyyəti xatırlamağa çalışacağıq və istəyənlər üçün ətraflı məlumat üçün keçidlərdən materiala dalmağı xahiş edirəm. Gəlin səyahətimizə Java dilinin spesifikasiyası ilə, yəni JLS ilə başlayaq: " 17.1. Sinxronizasiya ". Orada belə deyilir: Java-nı iplə məhv edə bilməzsiniz: II hissə - sinxronizasiya - 5Məlum oldu ki, iplər arasında sinxronizasiya məqsədi ilə Java “Monitor” adlı müəyyən mexanizmdən istifadə edir. Hər bir obyektin onunla əlaqəli monitoru var və iplər onu kilidləyə və ya kilidini aça bilər. Sonra Oracle saytında təlim təlimatı tapacağıq: “ Daxili Kilidlər və Sinxronizasiya ”. Bu dərslik izah edir ki, Java-da sinxronizasiya daxili kilid və ya monitor kilidi kimi tanınan daxili qurum ətrafında qurulur. Çox vaxt belə bir kilid sadəcə "monitor" adlanır. Bir daha görürük ki, Java-dakı hər bir obyektin onunla əlaqəli daxili kilidi var. Siz oxuya bilərsiniz " Java - Daxili Kilidlər və Sinxronizasiya ". Bundan sonra, Java-da bir obyektin monitorla necə əlaqələndirilə biləcəyini başa düşmək vacibdir. Java-dakı hər bir obyektin bir başlığı var - koddan proqramçı üçün mövcud olmayan, lakin virtual maşının obyektlərlə düzgün işləməsi üçün lazım olan bir növ daxili metadata. Obyekt başlığına belə görünən MarkWord daxildir: Java-nı iplə məhv edə bilməzsiniz: II hissə - sinxronizasiya - 6

https://edu.netbeans.org/contrib/slides/java-overview-and-java-se6.pdf

Habr-dan bir məqalə burada çox faydalıdır: " Bəs çox iş parçacığı necə işləyir? I hissə: sinxronizasiya ." Bu məqaləyə JDK bugtaker-dən tapşırıq blokunun xülasəsindən bir təsvir əlavə etmək lazımdır: “ JDK-8183909 ”. Eyni şeyi " JEP-8183909 " da oxuya bilərsiniz . Beləliklə, Java-da monitor bir obyektlə əlaqələndirilir və ip bu ipi blok edə bilər və ya onlar da "kilid al" deyirlər. Ən sadə misal:
public class HelloWorld{
    public static void main(String []args){
        Object object = new Object();
        synchronized(object) {
            System.out.println("Hello World");
        }
    }
}
Beləliklə, açar sözdən istifadə edərək, synchronizedcari ip (bu kod sətirlərinin yerinə yetirildiyi) obyektlə əlaqəli monitordan istifadə etməyə objectvə "kilid almağa" və ya "monitoru tutmağa" çalışır (ikinci seçim hətta üstünlükdür). Monitor üçün heç bir mübahisə yoxdursa (yəni başqa heç kim eyni obyektdə sinxronizasiya etmək istəmir), Java "qərəzli kilidləmə" adlı optimallaşdırmanı həyata keçirməyə cəhd edə bilər. Mark Word-də obyektin başlığında müvafiq teq və monitorun hansı ipə qoşulduğu qeydi olacaq. Bu, monitoru çəkərkən yükü azaldır. Monitor daha əvvəl başqa bir ipə bağlanıbsa, bu kilidləmə kifayət deyil. JVM növbəti kilidləmə növünə keçir - əsas kilidləmə. Müqayisə et və dəyişdir (CAS) əməliyyatlarından istifadə edir. Eyni zamanda, Mark Word-dəki başlıq artıq Mark Word-ün özünü saxlamır, lakin onun saxlanmasına keçid + etiket dəyişdirilir ki, JVM əsas kilidləmədən istifadə etdiyimizi başa düşsün. Bir neçə mövzunun monitoru üçün mübahisə varsa (biri monitoru tutub, ikincisi monitorun buraxılmasını gözləyir), onda Mark Word-dəki etiket dəyişir və Mark Word monitora istinad kimi saxlamağa başlayır. obyekt - JVM-nin bəzi daxili varlığı. JEP-də qeyd edildiyi kimi, bu halda, bu obyekti saxlamaq üçün Native Heap yaddaş sahəsində yer tələb olunur. Bu daxili obyektin saxlama yeri ilə əlaqə Mark Word obyektində yerləşəcəkdir. Beləliklə, gördüyümüz kimi, monitor həqiqətən çoxlu mövzuların paylaşılan resurslara girişinin sinxronizasiyasını təmin edən bir mexanizmdir. JVM-nin keçid etdiyi bu mexanizmin bir neçə tətbiqi var. Buna görə də, sadəlik üçün, bir monitor haqqında danışarkən, əslində kilidlərdən danışırıq. Java-nı iplə məhv edə bilməzsiniz: II hissə - sinxronizasiya - 7

Sinxronlaşdırılmış və kilidlə gözləyir

Monitor anlayışı, əvvəllər gördüyümüz kimi, "sinxronizasiya bloku" (yaxud da deyildiyi kimi, kritik bölmə) anlayışı ilə sıx bağlıdır. Bir misala baxaq:
public static void main(String[] args) throws InterruptedException {
	Object lock = new Object();

	Runnable task = () -> {
		synchronized (lock) {
			System.out.println("thread");
		}
	};

	Thread th1 = new Thread(task);
	th1.start();
	synchronized (lock) {
		for (int i = 0; i < 8; i++) {
			Thread.currentThread().sleep(1000);
			System.out.print("  " + i);
		}
		System.out.println(" ...");
	}
}
Burada əsas ip əvvəlcə tapşırığı yeni bir ipə göndərir, sonra dərhal kilidi "tutur" və onunla uzun bir əməliyyat həyata keçirir (8 saniyə). Bütün bu müddət ərzində tapşırıq yerinə yetirilməsi üçün bloka daxil ola bilməz synchronized, çünki kilid artıq işğal olunub. Əgər ip kilid ala bilmirsə, onu monitorda gözləyəcək. Onu alan kimi icrasını davam etdirəcək. Bir ip monitordan çıxanda kilidi buraxır. JVisualVM-də bu belə görünür: Java-nı iplə məhv edə bilməzsiniz: II hissə - sinxronizasiya - 8Gördüyünüz kimi, JVisualVM-də status "Monitor" adlanır, çünki ip bloklanır və monitoru tuta bilmir. Siz həmçinin koddakı ipin vəziyyətini öyrənə bilərsiniz, lakin bu vəziyyətin adı JVisualVM şərtləri ilə üst-üstə düşmür, baxmayaraq ki, onlar oxşardır. Bu halda, th1.getState()döngə BLOCKEDfor geri dönəcək , çünki Döngə işləyərkən, monitor iplə məşğul olur və ip bloklanır və kilid qaytarılana qədər işə davam edə bilməz. Sinxronizasiya bloklarına əlavə olaraq, bütün metod sinxronlaşdırıla bilər. Məsələn, sinifdən bir üsul : lockmainth1HashTable
public synchronized int size() {
	return count;
}
Bir zaman vahidində bu üsul yalnız bir iplə yerinə yetiriləcəkdir. Amma bizə kilid lazımdır, elə deyilmi? Bəli ehtiyacım var. Obyekt metodları vəziyyətində kilid olacaq this. Bu mövzuda maraqlı bir müzakirə var: " Sinxronlaşdırılmış Blok yerinə Sinxronlaşdırılmış Metoddan istifadə etməyin üstünlüyü varmı? ". Metod statikdirsə, o zaman kilid olmayacaq this(çünki statik metod üçün ola bilməz this), lakin sinif obyekti (Məsələn, Integer.class).

Gözləyin və monitorda gözləyin. Bütün üsulları bildirin və xəbərdar edin

Mövzuda monitora qoşulmuş başqa bir gözləmə üsulu var. sleepvə -dən fərqli olaraq joinonu sadəcə adlandırmaq olmaz. Və onun adıdır wait. Metod waitmonitorunda gözləmək istədiyimiz obyektdə icra olunur. Bir nümunəyə baxaq:
public static void main(String []args) throws InterruptedException {
	    Object lock = new Object();
	    // task будет ждать, пока его не оповестят через lock
	    Runnable task = () -> {
	        synchronized(lock) {
	            try {
	                lock.wait();
	            } catch(InterruptedException e) {
	                System.out.println("interrupted");
	            }
	        }
	        // После оповещения нас мы будем ждать, пока сможем взять лок
	        System.out.println("thread");
	    };
	    Thread taskThread = new Thread(task);
	    taskThread.start();
        // Ждём и после этого забираем себе лок, оповещаем и отдаём лок
	    Thread.currentThread().sleep(3000);
	    System.out.println("main");
	    synchronized(lock) {
	        lock.notify();
	    }
}
JVisualVM-də bu belə görünəcək: Bunun necə işlədiyini başa düşmək üçün metodlara istinad etdiyinizi Java-nı iplə məhv edə bilməzsiniz: II hissə - sinxronizasiya - 10xatırlamalısınız . Mövzu ilə əlaqəli metodların olması qəribə görünür . Ancaq cavab buradadır. Xatırladığımız kimi, Java-da hər bir obyektin bir başlığı var. Başlıq müxtəlif xidmət məlumatlarını, o cümlədən monitor haqqında məlumatı — kilidləmə vəziyyəti haqqında məlumatları ehtiva edir. Və xatırladığımız kimi, hər bir obyektin (yəni hər bir nümunənin) daxili kilid adlanan daxili JVM obyekti ilə əlaqəsi var ki, bu da monitor adlanır. Yuxarıdakı misalda tapşırıq təsvir edir ki, biz ilə əlaqəli monitorda sinxronizasiya blokuna daxil oluruq . Əgər bu monitorda kilid əldə etmək mümkündürsə, onda . Bu tapşırığı yerinə yetirən ip monitoru buraxacaq , lakin monitorda bildiriş gözləyən tellərin növbəsinə qoşulacaq . Bu ip növbəsi mahiyyəti daha düzgün əks etdirən WAIT-SET adlanır. Bu, növbədən daha çox dəstdir. Mövzu tapşırıq tapşırığı ilə yeni başlıq yaradır, onu işə salır və 3 saniyə gözləyir. Bu, yüksək ehtimalla yeni ipin ipdən əvvəl kilidi tutmasına və monitorda növbəyə durmasına imkan verir. Bundan sonra ipin özü sinxronizasiya blokuna daxil olur və monitorda ip haqqında bildiriş verir. Bildiriş göndərildikdən sonra mövzu monitoru buraxır və yeni başlıq (əvvəllər gözləyən) monitorun buraxılmasını gözlədikdən sonra icrasına davam edir. Bir anda yalnız mövzulardan birinə ( ) və ya növbədəki bütün mövzulara ( ) bildiriş göndərmək mümkündür . Daha ətraflı " Java-da notify() və notifyAll() arasındakı fərq " bölməsində oxuya bilərsiniz . Qeyd etmək vacibdir ki, bildiriş qaydası JVM tətbiqindən asılıdır. Ətraflı oxuya bilərsiniz " Aclığı bildiriş və bildirişlə necə həll etmək olar? ". Sinxronizasiya obyekt göstərilmədən həyata keçirilə bilər. Bu, kodun ayrıca bölməsi deyil, bütöv bir metod sinxronlaşdırıldıqda edilə bilər. Məsələn, statik metodlar üçün kilid sinif obyekti olacaq (vasitəsilə əldə edilir ): waitnotifyjava.lang.ObjectObjectlockwaitlocklockmainmainmainlockmainlocklocknotifynotifyAll.class
public static synchronized void printA() {
	System.out.println("A");
}
public static void printB() {
	synchronized(HelloWorld.class) {
		System.out.println("B");
	}
}
Kilidlərin istifadəsi baxımından hər iki üsul eynidir. Metod statik deyilsə, sinxronizasiya cərəyana görə instance, yəni uyğun olaraq həyata keçiriləcək this. Yeri gəlmişkən, əvvəllər dedik ki, metoddan istifadə edərək getStateipin statusunu əldə edə bilərsiniz. waitBelə ki, burada monitor tərəfindən növbəyə qoyulmuş bir mövzu var, əgər metod bir gözləmə müddətini təyin edərsə, status GÖZLƏYİR və ya TIMED_WAITING olacaqdır .Java-nı iplə məhv edə bilməzsiniz: II hissə - sinxronizasiya - 11

Bir ipin həyat dövrü

Gördüyümüz kimi, axın həyat axarında öz statusunu dəyişir. Əslində bu dəyişikliklər ipin həyat dövrüdür. Mövzu yeni yaradıldıqda, YENİ statusuna malikdir. Bu mövqedə o, hələ başlamamışdır və Java Thread Scheduler yeni mövzu haqqında hələ heç nə bilmir. Mövzu planlaşdırıcısının mövzu haqqında bilməsi üçün siz thread.start(). Sonra ip RUNNABLE vəziyyətinə keçəcək. İnternetdə Runnable və Running dövlətlərinin ayrıldığı çoxlu yanlış sxemlər var. Amma bu səhvdir, çünki... Java "çalışmağa hazır" və "çalışan" statusları arasında fərq qoymur. İp canlı olduqda, lakin aktiv olmadıqda (İşlənə bilməz), o, iki vəziyyətdən birində olur:
  • BLOCKED - qorunan bölməyə daxil olmağı gözləyir, yəni. bloka synchonized.
  • GÖZLƏYİR - şərtə əsasən başqa mövzu gözləyir. Şərt doğrudursa, mövzu planlaşdırıcı mövzunu işə salır.
Əgər mövzu zamanla gözləyirsə, o, TIMED_WAITING vəziyyətindədir. Mövzu artıq işləmirsə (müvəffəqiyyətlə tamamlanıb və ya istisna olmaqla), o, SONLANDIRILMIŞ statusuna keçir. Bir ipin vəziyyətini (vəziyyətini) tapmaq üçün metoddan istifadə olunur getState. Mövzularda, həmçinin, isAlivemövzu Sonlandırılmadığı təqdirdə doğru qaytaran bir üsul var.

LockSupport və ip dayanacağı

Java 1.6-dan bəri LockSupport adlı maraqlı mexanizm var idi . Java-nı iplə məhv edə bilməzsiniz: II hissə - sinxronizasiya - 12Bu sinif onu istifadə edən hər bir mövzu ilə "icazə" və ya icazəni əlaqələndirir. parkÇağırış zamanı eyni icazəni tutan icazə varsa, metod çağırışı dərhal qaytarılır. Əks halda bloklanır. Metodun çağırılması, unparkəgər o, artıq mövcud deyilsə, icazəni əlçatan edir. Yalnız 1 İcazə var Java API-də LockSupportmüəyyən Semaphore. Sadə bir misala baxaq:
import java.util.concurrent.Semaphore;
public class HelloWorldApp{

    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(0);
        try {
            semaphore.acquire();
        } catch (InterruptedException e) {
            // Просим разрешение и ждём, пока не получим его
            e.printStackTrace();
        }
        System.out.println("Hello, World!");
    }
}
Bu kod həmişəlik gözləyəcək, çünki semaforun indi 0 icazəsi var. Və kodla çağırıldıqda acquire(yəni, icazə tələb olunur), mövzu icazə alana qədər gözləyir. Gözlədiyimiz üçün onu emal etmək məcburiyyətindəyik InterruptedException. Maraqlıdır ki, semafor ayrı bir ip vəziyyətini həyata keçirir. JVisualVM-ə baxsaq görərik ki, bizim dövlət Wait deyil, Parkdır. Java-nı iplə məhv edə bilməzsiniz: II hissə - sinxronizasiya - 13Başqa bir misala baxaq:
public static void main(String[] args) throws InterruptedException {
        Runnable task = () -> {
            //Запаркуем текущий поток
            System.err.println("Will be Parked");
            LockSupport.park();
            // Как только нас распаркуют - начнём действовать
            System.err.println("Unparked");
        };
        Thread th = new Thread(task);
        th.start();
        Thread.currentThread().sleep(2000);
        System.err.println("Thread state: " + th.getState());

        LockSupport.unpark(th);
        Thread.currentThread().sleep(2000);
}
Mövzu statusu GÖZLƏYƏCƏK olacaq, lakin JVisualVM wait-dən synchronizedpark-dən fərqləndirir LockSupport. Bu niyə bu qədər vacibdir LockSupport? Gəlin yenidən Java API-ə keçək və Mövzu Durumu GÖZLƏYƏN- ə baxaq . Gördüyünüz kimi, ona daxil olmağın yalnız üç yolu var. 2 yol - bu waitjoin. Üçüncüsü isə LockSupport. Java-da kilidlər eyni prinsiplər üzərində qurulur LockSupportvə daha yüksək səviyyəli alətləri təmsil edir. Birini istifadə etməyə çalışaq. Məsələn, baxaq ReentrantLock:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class HelloWorld{

    public static void main(String []args) throws InterruptedException {
        Lock lock = new ReentrantLock();
        Runnable task = () -> {
            lock.lock();
            System.out.println("Thread");
            lock.unlock();
        };
        lock.lock();

        Thread th = new Thread(task);
        th.start();
        System.out.println("main");
        Thread.currentThread().sleep(2000);
        lock.unlock();
    }
}
Əvvəlki nümunələrdə olduğu kimi burada da hər şey sadədir. lockkiminsə resurs buraxmasını gözləyir. JVisualVM-ə baxsaq, görərik ki, mainip ona kilidi verənə qədər yeni başlıq dayanacaq. Kilidlər haqqında daha ətraflı burada oxuya bilərsiniz: " Java 8-də çox yivli proqramlaşdırma. İkinci hissə. Dəyişən obyektlərə girişin sinxronlaşdırılması " və " Java Lock API. İstifadə nəzəriyyəsi və nümunəsi ." Kilidlərin tətbiqini daha yaxşı başa düşmək üçün " Phaser Class " icmalında Phazer haqqında oxumaq faydalıdır . Müxtəlif sinxronizatorlar haqqında danışarkən, Habré-də “ Java.util.concurrent.* Sinxronizatorlara Referans ” məqaləsini oxumalısınız .

Ümumi

Bu araşdırmada Java-da mövzuların qarşılıqlı əlaqəsinin əsas yollarına baxdıq. Əlavə material: #Viaçeslav
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION