JavaRush /Java Blog /Random-TL /Hindi mo masisira ang Java gamit ang isang thread: Part I...

Hindi mo masisira ang Java gamit ang isang thread: Part II - synchronization

Nai-publish sa grupo

Panimula

Kaya, alam namin na may mga thread sa Java, na mababasa mo sa pagsusuri na " You Can't Spoil Java with a Thread: Part I - Threads ". Ang mga thread ay kinakailangan upang gawin ang trabaho nang sabay-sabay. Samakatuwid, malamang na ang mga thread ay sa paanuman ay nakikipag-ugnayan sa isa't isa. Unawain natin kung paano ito nangyayari at kung anong mga pangunahing kontrol ang mayroon tayo. Hindi mo masisira ang Java gamit ang isang thread: Part II - synchronization - 1

Magbigay

Ang Thread.yield() na paraan ay mahiwaga at bihirang gamitin. Mayroong maraming mga pagkakaiba-iba ng paglalarawan nito sa Internet. Hanggang sa punto na ang ilan ay sumulat tungkol sa ilang uri ng pila ng mga thread, kung saan bababa ang thread na isinasaalang-alang ang kanilang mga priyoridad. May nagsusulat na babaguhin ng thread ang katayuan nito mula sa pagtakbo patungo sa runnable (bagaman walang dibisyon sa mga status na ito, at hindi nakikilala ng Java ang mga ito). Ngunit sa katotohanan, ang lahat ay mas hindi alam at, sa isang kahulugan, mas simple. Hindi mo masisira ang Java gamit ang isang thread: Part II - synchronization - 2Sa paksa ng dokumentasyon ng pamamaraan, yieldmayroong isang bug " JDK-6416721: (spec thread) Ayusin ang Thread.yield() javadoc ". Kung babasahin mo ito, malinaw na sa katunayan ang pamamaraan yielday nagbibigay lamang ng ilang rekomendasyon sa Java thread scheduler na ang thread na ito ay maaaring bigyan ng mas kaunting oras ng pagpapatupad. Ngunit kung ano talaga ang mangyayari, kung maririnig ng scheduler ang rekomendasyon at kung ano ang gagawin nito sa pangkalahatan ay depende sa pagpapatupad ng JVM at ng operating system. O marahil mula sa ilang iba pang mga kadahilanan. Ang lahat ng pagkalito ay malamang dahil sa muling pag-iisip ng multithreading sa panahon ng pagbuo ng wikang Java. Maaari kang magbasa nang higit pa sa pagsusuri na " Maikling Panimula sa Java Thread.yield() ".

Sleep - Natutulog na thread

Maaaring makatulog ang isang thread sa panahon ng pagpapatupad nito. Ito ang pinakasimpleng uri ng pakikipag-ugnayan sa ibang mga thread. Ang operating system kung saan naka-install ang Java virtual machine, kung saan ang Java code ay pinaandar, ay may sariling thread scheduler, na tinatawag na Thread Scheduler. Siya ang nagpapasya kung aling thread ang tatakbo kung kailan. Ang programmer ay hindi maaaring direktang makipag-ugnayan sa scheduler na ito mula sa Java code, ngunit maaari niyang, sa pamamagitan ng JVM, hilingin sa scheduler na i-pause ang thread nang ilang sandali, na "itulog ito." Maaari kang magbasa ng higit pa sa mga artikulong " Thread.sleep() " at " Paano gumagana ang Multithreading ". Bukod dito, maaari mong malaman kung paano gumagana ang mga thread sa Windows OS: " Mga Internal ng Windows Thread ". Ngayon ay makikita natin ito ng ating mga mata. I-save natin ang sumusunod na code sa isang file 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();
    }
}
Tulad ng nakikita mo, mayroon kaming isang gawain na naghihintay ng 60 segundo, pagkatapos nito magtatapos ang programa. Kami ay nag-compile javac HelloWorldApp.javaat tumatakbo java HelloWorldApp. Mas mainam na ilunsad sa isang hiwalay na window. Halimbawa, sa Windows ito ay magiging ganito: start java HelloWorldApp. Gamit ang jps command, nalaman namin ang PID ng proseso at buksan ang listahan ng mga thread gamit ang jvisualvm --openpid pidПроцесса: Hindi mo masisira ang Java gamit ang isang thread: Part II - synchronization - 3Tulad ng nakikita mo, ang aming thread ay pumasok sa Sleeping status. Sa katunayan, ang pagtulog sa kasalukuyang thread ay maaaring gawin nang mas maganda:
try {
	TimeUnit.SECONDS.sleep(60);
	System.out.println("Waked up");
} catch (InterruptedException e) {
	e.printStackTrace();
}
Marahil ay napansin mo na nagproseso kami sa lahat ng dako InterruptedException? Intindihin natin kung bakit.

Nakakaabala sa isang thread o Thread.interrupt

Ang bagay ay habang ang thread ay naghihintay sa isang panaginip, maaaring may gustong matakpan ang paghihintay na ito. Sa kasong ito, pinangangasiwaan namin ang gayong pagbubukod. Ginawa ito pagkatapos Thread.stopideklarang Deprecated ang paraan, i.e. lipas na at hindi kanais-nais na gamitin. Ang dahilan para dito ay kapag tinawag ang pamamaraan, stopang thread ay "pinatay" lamang, na napaka hindi mahuhulaan. Hindi namin alam kung kailan titigil ang daloy, hindi namin magagarantiya ang pagkakapare-pareho ng data. Isipin na nagsusulat ka ng data sa isang file at pagkatapos ay masisira ang stream. Samakatuwid, napagpasyahan nila na mas makatuwirang huwag patayin ang daloy, ngunit ipaalam ito na dapat itong maantala. Kung paano mag-react dito ay nakasalalay sa mismong daloy. Higit pang mga detalye ang makikita sa Oracle's " Why is Thread.stop deprecated? " Tingnan natin ang isang halimbawa:
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();
}
Sa halimbawang ito, hindi kami maghihintay ng 60 segundo, ngunit agad na magpi-print ng 'Nagambala'. Ito ay dahil tinawag namin ang pamamaraan ng thread interrupt. Ang pamamaraang ito ay nagtatakda ng "internal na bandila na tinatawag na interrupt status". Ibig sabihin, ang bawat thread ay may panloob na bandila na hindi direktang naa-access. Ngunit mayroon kaming mga katutubong pamamaraan para sa pakikipag-ugnayan sa watawat na ito. Ngunit hindi ito ang tanging paraan. Ang isang thread ay maaaring nasa proseso ng pagpapatupad, hindi naghihintay para sa isang bagay, ngunit simpleng gumaganap ng mga aksyon. Ngunit maaari itong magbigay na nais nilang kumpletuhin ito sa isang tiyak na punto sa gawain nito. Halimbawa:
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();
}
Sa halimbawa sa itaas, makikita mo na ang loop whileay tatakbo hanggang ang thread ay magambala sa labas. Ang mahalagang bagay na dapat malaman tungkol sa isInterrupted na flag ay kung mahuli natin ito InterruptedException, ang flag isInterrupteday mare-reset, at pagkatapos isInterrupteday magbabalik ito ng false. Mayroon ding static na pamamaraan sa klase ng Thread na nalalapat lamang sa kasalukuyang thread - Thread.interrupted() , ngunit ni-reset ng pamamaraang ito ang flag sa false! Maaari mong basahin ang higit pa sa kabanata na " Thread Interruption ".

Sumali — Naghihintay na makumpleto ang isa pang thread

Ang pinakasimpleng uri ng paghihintay ay naghihintay para sa isa pang thread na makumpleto.
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");
}
Sa halimbawang ito, matutulog ang bagong thread sa loob ng 5 segundo. Kasabay nito, maghihintay ang pangunahing thread hanggang sa magising ang natutulog na thread at matapos ang trabaho nito. Kung titingnan mo ang JVisualVM, magiging ganito ang estado ng thread: Hindi mo masisira ang Java gamit ang isang thread: Part II - synchronization - 4Salamat sa mga tool sa pagsubaybay, makikita mo kung ano ang nangyayari sa thread. Ang pamamaraan joinay medyo simple, dahil ito ay isang paraan lamang na may java code na nagpapatupad waithabang ang thread kung saan ito tinatawag ay buhay. Kapag namatay ang thread (sa pagtatapos), ang paghihintay ay wawakasan. Iyan ang buong magic ng pamamaraan join. Samakatuwid, lumipat tayo sa pinaka-kagiliw-giliw na bahagi.

Monitor ng Konsepto

Sa multithreading mayroong isang bagay tulad ng Monitor. Sa pangkalahatan, ang salitang monitor ay isinalin mula sa Latin bilang "tagapangasiwa" o "tagapangasiwa." Sa loob ng balangkas ng artikulong ito, susubukan naming tandaan ang kakanyahan, at para sa mga nais, hinihiling ko sa iyo na sumisid sa materyal mula sa mga link para sa mga detalye. Simulan natin ang ating paglalakbay sa pagtutukoy ng wikang Java, iyon ay, sa JLS: " 17.1. Synchronization ". Sinasabi nito ang sumusunod: Hindi mo masisira ang Java gamit ang isang thread: Part II - synchronization - 5Lumalabas na para sa layunin ng pag-synchronize sa pagitan ng mga thread, ang Java ay gumagamit ng isang tiyak na mekanismo na tinatawag na "Monitor". Ang bawat bagay ay may monitor na nauugnay dito, at maaaring i-lock o i-unlock ito ng mga thread. Susunod, makakahanap kami ng tutorial sa pagsasanay sa website ng Oracle: " Intrinsic Locks and Synchronization ". Ipinapaliwanag ng tutorial na ito na ang pag-synchronize sa Java ay binuo sa paligid ng isang panloob na entity na kilala bilang isang intrinsic lock o monitor lock. Kadalasan ang gayong lock ay tinatawag na "monitor". Muli nating nakikita na ang bawat bagay sa Java ay may intrinsic lock na nauugnay dito. Mababasa mo ang " Java - Intrinsic Locks and Synchronization ". Susunod, mahalagang maunawaan kung paano maiuugnay ang isang bagay sa Java sa isang monitor. Ang bawat object sa Java ay may header - isang uri ng panloob na metadata na hindi available sa programmer mula sa code, ngunit kailangan ng virtual machine na gumana sa mga bagay nang tama. Kasama sa header ng object ang isang MarkWord na ganito ang hitsura: Hindi mo masisira ang Java gamit ang isang thread: Part II - synchronization - 6

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

Ang isang artikulo mula sa Habr ay lubhang kapaki-pakinabang dito: " Ngunit paano gumagana ang multithreading? Bahagi I: pag-synchronize ." Sa artikulong ito ito ay nagkakahalaga ng pagdaragdag ng isang paglalarawan mula sa Buod ng bloke ng gawain mula sa JDK bugtaker: " JDK-8183909 ". Mababasa mo ang parehong bagay sa " JEP-8183909 ". Kaya, sa Java, ang isang monitor ay nauugnay sa isang bagay at maaaring harangan ng thread ang thread na ito, o sinasabi rin nila na "kumuha ng lock". Ang pinakasimpleng halimbawa:
public class HelloWorld{
    public static void main(String []args){
        Object object = new Object();
        synchronized(object) {
            System.out.println("Hello World");
        }
    }
}
Kaya, gamit ang keyword, synchronizedang kasalukuyang thread (kung saan isinagawa ang mga linya ng code na ito) ay sumusubok na gamitin ang monitor na nauugnay sa bagay objectat "kumuha ng isang lock" o "kuhanan ang monitor" (ang pangalawang pagpipilian ay mas kanais-nais). Kung walang pagtatalo para sa monitor (ibig sabihin, walang ibang gustong mag-synchronize sa parehong bagay), maaaring subukan ng Java na magsagawa ng pag-optimize na tinatawag na "biased locking". Ang pamagat ng bagay sa Mark Word ay maglalaman ng kaukulang tag at isang talaan kung saan ang thread ay naka-attach sa monitor. Binabawasan nito ang overhead kapag kinukuha ang monitor. Kung ang monitor ay nakatali na sa isa pang thread bago, kung gayon ang pag-lock na ito ay hindi sapat. Lumipat ang JVM sa susunod na uri ng pag-lock - pangunahing pag-lock. Gumagamit ito ng mga pagpapatakbo ng compare-and-swap (CAS). Kasabay nito, ang header sa Mark Word ay hindi na nag-iimbak mismo ng Mark Word, ngunit isang link sa imbakan nito + ang tag ay binago upang maunawaan ng JVM na gumagamit kami ng pangunahing pag-lock. Kung mayroong pagtatalo para sa monitor ng ilang mga thread (nakuha ng isa ang monitor, at ang pangalawa ay naghihintay na mailabas ang monitor), pagkatapos ay nagbabago ang tag sa Mark Word, at ang Mark Word ay nagsimulang mag-imbak ng isang reference sa monitor bilang isang bagay - ilang panloob na entity ng JVM. Gaya ng nakasaad sa JEP, sa kasong ito, kinakailangan ang espasyo sa lugar ng memorya ng Native Heap upang maiimbak ang entity na ito. Ang link sa lokasyon ng imbakan ng panloob na entity na ito ay matatagpuan sa object ng Mark Word. Kaya, tulad ng nakikita natin, ang monitor ay talagang isang mekanismo para sa pagtiyak ng pag-synchronize ng pag-access ng maraming mga thread sa mga nakabahaging mapagkukunan. Mayroong ilang mga pagpapatupad ng mekanismong ito na pinapalitan ng JVM. Samakatuwid, para sa pagiging simple, kapag pinag-uusapan ang isang monitor, talagang pinag-uusapan natin ang tungkol sa mga kandado. Hindi mo masisira ang Java gamit ang isang thread: Part II - synchronization - 7

Naka-synchronize at naghihintay sa pamamagitan ng lock

Ang konsepto ng isang monitor, tulad ng nakita natin dati, ay malapit na nauugnay sa konsepto ng isang "block ng pag-synchronize" (o, kung tawagin din ito, isang kritikal na seksyon). Tingnan natin ang isang halimbawa:
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(" ...");
	}
}
Dito, ang pangunahing thread ay unang nagpapadala ng gawain sa isang bagong thread, at pagkatapos ay agad na "kinukuha" ang lock at nagsasagawa ng mahabang operasyon kasama nito (8 segundo). Sa lahat ng oras na ito, hindi makapasok ang gawain sa block para sa pagpapatupad nito synchronized, dahil okupado na ang lock. Kung hindi makakuha ng lock ang isang thread, maghihintay ito sa monitor para dito. Sa sandaling matanggap ito, magpapatuloy ito sa pagpapatupad. Kapag ang isang thread ay umalis sa monitor, inilalabas nito ang lock. Sa JVisualVM magiging ganito ang hitsura: Hindi mo masisira ang Java gamit ang isang thread: Part II - synchronization - 8Gaya ng nakikita mo, ang status sa JVisualVM ay tinatawag na "Monitor" dahil ang thread ay naka-block at hindi maaaring sakupin ang monitor. Maaari mo ring malaman ang estado ng thread sa code, ngunit ang pangalan ng estado na ito ay hindi nag-tutugma sa mga tuntunin ng JVisualVM, kahit na magkapareho sila. Sa kasong ito, th1.getState()ang loop foray babalik BLOCKED , dahil Habang tumatakbo ang loop, ang monitor lockay inookupahan mainng thread, at ang thread th1ay naharang at hindi maaaring magpatuloy sa paggana hanggang sa maibalik ang lock. Bilang karagdagan sa mga bloke ng pag-synchronize, maaaring i-synchronize ang isang buong paraan. Halimbawa, isang pamamaraan mula sa klase HashTable:
public synchronized int size() {
	return count;
}
Sa isang yunit ng oras, ang pamamaraang ito ay isasagawa lamang ng isang thread. Pero kailangan natin ng lock diba? Oo kailangan ko ito. Sa kaso ng mga object method, ang lock ay this. Mayroong isang kawili-wiling talakayan sa paksang ito: " Mayroon bang kalamangan na gumamit ng Naka-synchronize na Paraan sa halip na isang Naka-synchronize na Block? ". Kung ang pamamaraan ay static, kung gayon ang lock ay hindi magiging this(dahil para sa isang static na pamamaraan ay hindi ito maaaring this), ngunit ang class object (Halimbawa, Integer.class).

Maghintay at maghintay sa monitor. Ang abisuhan at ipaalam angLahat ng mga pamamaraan

Ang thread ay may isa pang paraan ng paghihintay, na konektado sa monitor. Hindi tulad sleepng at join, hindi ito basta-basta matatawag. At ang pangalan niya ay wait. Ang pamamaraan ay isinasagawa waitsa bagay na kung saan ang monitor ay nais naming maghintay. Tingnan natin ang isang halimbawa:
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();
	    }
}
Sa JVisualVM magiging ganito ang hitsura: Hindi mo masisira ang Java gamit ang isang thread: Part II - synchronization - 10Upang maunawaan kung paano ito gumagana, dapat mong tandaan na ang mga pamamaraan waitay tumutukoy notifysa java.lang.Object. Tila kakaiba na ang mga pamamaraan na nauugnay sa thread ay nasa Object. Ngunit narito ang sagot. Tulad ng naaalala natin, ang bawat bagay sa Java ay may isang header. Naglalaman ang header ng iba't ibang impormasyon ng serbisyo, kabilang ang impormasyon tungkol sa monitor—data tungkol sa estado ng pag-lock. At gaya ng natatandaan natin, ang bawat object (i.e. bawat instance) ay may kaugnayan sa isang panloob na JVM entity na tinatawag na intrinsic lock, na tinatawag ding monitor. Sa halimbawa sa itaas, ang gawain ay naglalarawan na ipinasok namin ang synchronization block sa monitor na nauugnay sa lock. Kung posible na makakuha ng lock sa monitor na ito, kung gayon wait. Ang thread na nagsasagawa ng gawaing ito ay maglalabas ng monitor lock, ngunit sasali sa pila ng mga thread na naghihintay ng abiso sa monitor lock. Ang pila ng mga thread na ito ay tinatawag na WAIT-SET, na mas wastong sumasalamin sa kakanyahan. Ito ay higit pa sa isang set kaysa sa isang pila. Lumilikha ang thread mainng bagong thread na may gawaing gawain, sisimulan ito at maghihintay ng 3 segundo. Nagbibigay-daan ito, na may mataas na antas ng posibilidad, ng isang bagong thread na kunin ang lock bago ang thread mainat pumila sa monitor. Pagkatapos kung saan ang thread mainmismo ay pumapasok sa synchronization block lockat nagsasagawa ng abiso ng thread sa monitor. Pagkatapos maipadala ang abiso, mainilalabas ng thread ang monitor lock, at ang bagong thread (na kanina ay naghihintay) lockay patuloy na gumagana pagkatapos maghintay na ilabas ang monitor. Posibleng magpadala ng notification sa isa lang sa mga thread ( notify) o sa lahat ng thread sa queue nang sabay-sabay ( notifyAll). Maaari kang magbasa nang higit pa sa " Pagkakaiba sa pagitan ng notify() at notifyAll() sa Java ". Mahalagang tandaan na ang order ng notification ay nakasalalay sa pagpapatupad ng JVM. Maaari kang magbasa ng higit pa sa " Paano malutas ang gutom sa pamamagitan ng pag-abiso at pag-abiso sa lahat? ". Maaaring isagawa ang pag-synchronize nang hindi tinukoy ang isang bagay. Magagawa ito kapag hindi naka-synchronize ang isang hiwalay na seksyon ng code, ngunit isang buong pamamaraan. Halimbawa, para sa mga static na pamamaraan ang lock ay ang class object (nakuha sa pamamagitan ng .class):
public static synchronized void printA() {
	System.out.println("A");
}
public static void printB() {
	synchronized(HelloWorld.class) {
		System.out.println("B");
	}
}
Sa mga tuntunin ng paggamit ng mga kandado, ang parehong mga pamamaraan ay pareho. Kung ang pamamaraan ay hindi static, ang pag-synchronize ay isasagawa ayon sa kasalukuyang instance, iyon ay, ayon sa this. Sa pamamagitan ng paraan, mas maaga namin sinabi na gamit ang paraan getStatemaaari mong makuha ang katayuan ng isang thread. Kaya't narito ang isang thread na nakapila ng monitor, ang status ay WAITING o TIMED_WAITING kung ang pamamaraan ay waittinukoy ng isang limitasyon sa oras ng paghihintay. Hindi mo masisira ang Java gamit ang isang thread: Part II - synchronization - 11

Lifecycle ng isang thread

Gaya ng nakita natin, nagbabago ang katayuan ng daloy sa takbo ng buhay. Sa esensya, ang mga pagbabagong ito ay ang ikot ng buhay ng thread. Kapag kakagawa lang ng thread, may BAGONG status ito. Sa posisyong ito, hindi pa ito nagsisimula at ang Java Thread Scheduler ay wala pang alam tungkol sa bagong thread. Upang malaman ng thread scheduler ang tungkol sa isang thread, dapat mong tawagan ang thread.start(). Pagkatapos ang thread ay mapupunta sa RUNNABLE na estado. Maraming maling scheme sa Internet kung saan pinaghihiwalay ang Runnable at Running states. Ngunit ito ay isang pagkakamali, dahil... Hindi pinag-iiba ng Java ang mga status na "ready to run" at "running". Kapag ang isang thread ay buhay ngunit hindi aktibo (hindi Runnable), ito ay nasa isa sa dalawang estado:
  • BLOCKED - naghihintay ng pagpasok sa isang protektadong seksyon, i.e. sa synchonizedblock.
  • NAGHIHINTAY - naghihintay ng isa pang thread batay sa isang kundisyon. Kung totoo ang kundisyon, sisimulan ng thread scheduler ang thread.
Kung ang isang thread ay naghihintay ayon sa oras, ito ay nasa TIMED_WAITING na estado. Kung ang thread ay hindi na tumatakbo (matagumpay na nakumpleto o may pagbubukod), ito ay mapupunta sa status na TERMINATED. Upang malaman ang estado ng isang thread (ang estado nito), ang pamamaraan ay ginagamit getState. Ang mga thread ay mayroon ding paraan isAlivena nagbabalik ng totoo kung ang thread ay hindi Natapos.

LockSupport at paradahan ng thread

Dahil ang Java 1.6 ay mayroong isang kawili-wiling mekanismo na tinatawag na LockSupport . Hindi mo masisira ang Java gamit ang isang thread: Part II - synchronization - 12Ang klase na ito ay nag-uugnay ng "permit" o pahintulot sa bawat thread na gumagamit nito. Ang tawag sa pamamaraan parkay babalik kaagad kung may magagamit na permit, na sumasakop sa parehong permiso sa panahon ng tawag. Kung hindi, ito ay naharang. Ang pagtawag sa pamamaraan unparkay ginagawang available ang permit kung hindi pa ito magagamit. Mayroon lamang 1 Permit. Sa Java API, LockSupportisang tiyak na Semaphore. Tingnan natin ang isang simpleng halimbawa:
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!");
    }
}
Ang code na ito ay maghihintay magpakailanman dahil ang semaphore ay mayroon na ngayong 0 permit. At kapag tinawag sa code acquire(i.e., humiling ng pahintulot), ang thread ay naghihintay hanggang sa makatanggap ito ng pahintulot. Dahil naghihintay kami, obligado kaming iproseso ito InterruptedException. Kapansin-pansin, ang isang semaphore ay nagpapatupad ng isang hiwalay na estado ng thread. Kung titingnan natin sa JVisualVM, makikita natin na ang ating estado ay hindi Wait, kundi Park. Hindi mo masisira ang Java gamit ang isang thread: Part II - synchronization - 13Tingnan natin ang isa pang halimbawa:
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);
}
Ang status ng thread ay MAGHIHINTAY, ngunit ang JVisualVM ay nakikilala waitsa pagitan ng mula synchronizedat parkmula sa LockSupport. Bakit napakahalaga ng isang ito LockSupport? Bumalik tayo muli sa Java API at tingnan ang Thread State WAITING . Tulad ng nakikita mo, mayroon lamang tatlong paraan upang makapasok dito. 2 paraan - ito waitat join. At ang pangatlo ay LockSupport. Ang mga lock sa Java ay binuo sa parehong mga prinsipyo LockSupportat kumakatawan sa mga mas mataas na antas ng mga tool. Subukan nating gumamit ng isa. Tingnan natin, halimbawa, sa 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();
    }
}
Tulad ng sa mga nakaraang halimbawa, ang lahat ay simple dito. locknaghihintay para sa isang tao na maglabas ng isang mapagkukunan. Kung titingnan natin sa JVisualVM, makikita natin na ang bagong thread ay ipaparada hanggang sa mainibigay ito ng thread ang lock. Maaari kang magbasa ng higit pa tungkol sa mga lock dito: " Multithreaded programming sa Java 8. Ikalawang bahagi. Pag-synchronize ng access sa mga nababagong bagay " at " Java Lock API. Teorya at halimbawa ng paggamit ." Upang mas maunawaan ang pagpapatupad ng mga kandado, kapaki-pakinabang na basahin ang tungkol sa Phazer sa pangkalahatang-ideya na " Phaser Class ". At pagsasalita tungkol sa iba't ibang mga synchronizer, dapat mong basahin ang artikulo sa Habré " Java.util.concurrent.* Synchronizers Reference ".

Kabuuan

Sa pagsusuring ito, tiningnan namin ang mga pangunahing paraan ng pakikipag-ugnayan ng mga thread sa Java. Karagdagang materyal: #Viacheslav
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION