Programmēšana

Java 101: Java pavedienu izpratne, 3. daļa: Vītņu plānošana un gaidīšana / paziņošana

Šomēnes es turpinu savu četrdaļīgo ievadu Java pavedienos, koncentrējoties uz pavedienu plānošanu, gaidīšanas / paziņošanas mehānismu un pavedienu pārtraukšanu. Jūs izpētīsit, kā JVM, vai operētājsistēmas pavedienu plānotājs izvēlas nākamo pavedienu izpildei. Kā jūs atklāsiet, prioritāte ir svarīga pavedienu plānotāja izvēlei. Jūs pārbaudīsit, kā pavediens gaida, līdz tas saņem paziņojumu no cita pavediena, pirms tas turpina izpildi, un uzzināsiet, kā izmantot gaidīšanas / paziņošanas mehānismu divu pavedienu izpildes koordinēšanai ražotāja un patērētāja attiecībās. Visbeidzot, jūs uzzināsiet, kā priekšlaicīgi pamodināt miega vai gaidīšanas pavedienu pavedienu pārtraukšanai vai citiem uzdevumiem. Es arī iemācīšu jums, kā pavediens, kas nedz guļ, nedz gaida, uztver pārtraukuma pieprasījumu no cita pavediena.

Ņemiet vērā, ka šis raksts (daļa no JavaWorld arhīva) 2013. gada maijā tika atjaunināts ar jauniem kodu sarakstiem un lejupielādējamu avota kodu.

Izpratne par Java pavedieniem - izlasiet visu sēriju

  • 1. daļa: Vītņu un skrējienu iepazīstināšana
  • 2. daļa: Sinhronizācija
  • 3. daļa: Vītņu plānošana, gaidīšana / paziņošana un pavedienu pārtraukšana
  • 4. daļa: pavedienu grupas, svārstīgums, lokālie pavedienu mainīgie, taimeri un pavedienu bojāeja

Vītņu plānošana

Idealizētā pasaulē visiem programmu pavedieniem būtu savi procesori, kuros darboties. Līdz brīdim, kad datoriem ir tūkstošiem vai miljonu procesoru, pavedieniem bieži vien ir jāpiedalās vienā vai vairākos procesoros. Vai nu JVM, vai pamata platformas operētājsistēma atšifrē, kā procesora resursu koplietot starp pavedieniem - uzdevumu, kas pazīstams kā pavedienu plānošana. Tā JVM vai operētājsistēmas daļa, kas veic pavedienu plānošanu, ir a pavedienu plānotājs.

Piezīme: Lai vienkāršotu diskusiju par pavedienu plānošanu, es koncentrējos uz pavedienu plānošanu viena procesora kontekstā. Jūs varat ekstrapolēt šo diskusiju vairākiem procesoriem; Es atstāju šo uzdevumu jums.

Atcerieties divus svarīgus punktus par pavedienu plānošanu:

  1. Java neliek VM plānot pavedienus noteiktā veidā vai satur pavedienu plānotāju. Tas nozīmē no platformas atkarīgu pavedienu plānošanu. Tādēļ, rakstot Java programmu, kuras uzvedība ir atkarīga no pavedienu plānošanas un kurai konsekventi jādarbojas dažādās platformās, jums jāievēro piesardzība.
  2. Par laimi, rakstot Java programmas, jums ir jādomā par to, kā Java ieplāno pavedienus tikai tad, ja vismaz viens no jūsu programmas pavedieniem intensīvi izmanto procesoru ilgu laiku un šīs pavediena izpildes starpposma rezultāti izrādās svarīgi. Piemēram, sīklietotne satur pavedienu, kas dinamiski izveido attēlu. Periodiski vēlaties, lai gleznas pavediens uzzīmētu šī attēla pašreizējo saturu, lai lietotājs varētu redzēt, kā attēls virzās. Lai nodrošinātu, ka aprēķina pavediens nemonopolizē procesoru, apsveriet pavedienu plānošanu.

Pārbaudiet programmu, kas izveido divus procesorietilpīgus pavedienus:

Saraksts 1. SchedDemo.java

// SchedDemo.java klase SchedDemo {public static void main (String [] args) {new CalcThread ("CalcThread A"). Start (); jauns CalcThread ("CalcThread B"). start (); }} klase CalcThread paplašina pavedienu {CalcThread (virknes nosaukums) {// nodot nosaukumu pavedienam. super (vārds); } double calcPI () {Būla negatīvs = patiess; dubultā pi = 0,0; par (int i = 3; i <100000; i + = 2) {ja (negatīvs) pi - = (1,0 / i); cits pi + = (1,0 / i); negatīvs =! negatīvs; } pi + = 1,0; pi * = 4,0; atgriešanās pi; } public void run () {for (int i = 0; i <5; i ++) System.out.println (getName () + ":" + calcPI ()); }}

SchedDemo izveido divus pavedienus, no kuriem katrs aprēķina pi vērtību (piecas reizes) un izdrukā katru rezultātu. Atkarībā no tā, kā JVM ieviešana ieplāno pavedienus, iespējams, redzēsit šādu izvadi:

CalcThread A: 3,1415726535897894 CalcThread B: 3,1415726535897894 CalcThread A: 3,1415726535897894 CalcThread A: 3,1415726535897894 CalcThread B: 3,1415726535897894 CalcThread A: 3,1415726535897894 CalcThread A: 3,1415726535897894 CalcThread B: 3,1415726535897894 CalcThread B: 3,1415726535897894 CalcThread B: 3,1415726535897894

Saskaņā ar iepriekš minēto izvadi, pavedienu plānotājs dala procesoru starp abiem pavedieniem. Tomēr jūs varētu redzēt līdzīgu izvadi:

CalcThread A: 3,1415726535897894 CalcThread A: 3,1415726535897894 CalcThread A: 3,1415726535897894 CalcThread A: 3,1415726535897894 CalcThread A: 3,1415726535897894 CalcThread B: 3,1415726535897894 CalcThread B: 3,1415726535897894 CalcThread B: 3,1415726535897894 CalcThread B: 3,1415726535897894 CalcThread B: 3,1415726535897894

Iepriekš minētā izeja parāda pavedienu plānotāju, kas dod priekšroku vienam pavedienam. Divi iepriekš minētie rezultāti parāda divas vispārīgas pavedienu plānotāju kategorijas: zaļā un vietējā. Es izpētīšu viņu uzvedības atšķirības nākamajās sadaļās. Apspriežot katru kategoriju, es atsaucos pavedienu stāvokļi, no kuriem ir četri:

  1. Sākotnējais stāvoklis: Programma ir izveidojusi pavediena pavediena objektu, taču pavediens vēl nepastāv, jo pavediena objekts ir sākt() metode vēl nav izsaukta.
  2. Darbināms stāvoklis: Šis ir pavediena noklusējuma stāvoklis. Pēc zvana uz sākt() Pabeidz, pavediens kļūst palaists neatkarīgi no tā, vai šis pavediens darbojas, tas ir, izmantojot procesoru. Lai gan daudzi pavedieni varētu būt palaisti, pašlaik darbojas tikai viens. Vītņu plānotāji nosaka, kuru palaisto pavedienu piešķirt procesoram.
  3. Bloķēts stāvoklis: Kad pavediens izpilda Gulēt(), pagaidiet ()vai pievienoties () metodes, kad pavediens mēģina nolasīt datus, kas vēl nav pieejami no tīkla, un kad pavediens gaida bloķēšanas iegūšanu, šī pavediens ir bloķēts: tas nedz darbojas, nedz darbojas. (Jūs droši vien varat iedomāties citas reizes, kad pavediens gaidītu, lai kaut kas notiktu.) Kad bloķēts pavediens atbloķē, šis pavediens pāriet uz skrienamo stāvokli.
  4. Galīgais stāvoklis: Kad izpilde atstāj pavedienu palaist () metodi, šis pavediens ir beigu stāvoklī. Citiem vārdiem sakot, pavediens pārstāj pastāvēt.

Kā pavedienu plānotājs izvēlas, kuru palaisto pavedienu palaist? Es sāku atbildēt uz šo jautājumu, apspriežot zaļo pavedienu plānošanu. Es pabeidzu atbildi, apspriežot vietējo pavedienu plānošanu.

Zaļo pavedienu plānošana

Ne visas operētājsistēmas, piemēram, senā Microsoft Windows 3.1 saistītā sistēma, atbalsta pavedienus. Šādām sistēmām Sun Microsystems var izstrādāt JVM, kas sadala tā vienīgo izpildes pavedienu vairākos pavedienos. JVM (nevis pamata platformas operētājsistēma) piegādā vītņu loģiku un satur pavedienu plānotāju. JVM pavedieni ir zaļi pavedieni, vai lietotāju pavedieni.

JVM pavedienu plānotājs zaļos pavedienus ieplāno atbilstoši prioritāte- pavediena relatīvā nozīme, kuru jūs izsakāt kā veselu skaitli no precīzi definēta vērtību diapazona. Parasti JVM pavedienu plānotājs izvēlas visaugstākās prioritātes pavedienu un ļauj šim pavedienam darboties, līdz tas vai nu beidzas, vai bloķējas. Tajā laikā pavedienu plānotājs izvēlas nākamās augstākās prioritātes pavedienu. Šis pavediens (parasti) darbojas, līdz tas beidzas vai bloķējas. Ja pavediena darbības laikā augstākas prioritātes pavediens tiek atbloķēts (iespējams, ir beidzies augstākas prioritātes pavediena miega laiks), pavedienu plānotājs mēģinājumi, vai pārtrauc zemākas prioritātes pavedienu un piešķir atbloķētu augstākas prioritātes pavedienu procesoram.

Piezīme: Skrienamais pavediens ar visaugstāko prioritāti ne vienmēr darbosies. Lūk Java valodas specifikācija 'uzņemas prioritāti:

Katram pavedienam ir prioritāte. Ja notiek konkurence par resursu apstrādi, pavedieni ar augstāku prioritāti parasti tiek izpildīti, nevis pavedieni ar zemāku prioritāti. Šāda preference tomēr negarantē, ka vienmēr darbosies augstākās prioritātes pavediens, un pavedienu prioritātes nevar izmantot, lai droši īstenotu savstarpēju izslēgšanu.

Šī atzīšana daudz saka par zaļo diegu JVM ieviešanu. Šie JVM nevar atļauties pavedienus bloķēt, jo tas sasaistītu JVM vienīgo izpildes pavedienu. Tāpēc, ja pavedienam ir jābloķējas, piemēram, kad šī pavediens lasa datus, kas lēnām pienāk no faila, JVM var apturēt pavediena izpildi un izmantot aptaujas mehānismu, lai noteiktu datu saņemšanas laiku. Kamēr pavediens paliek apturēts, JVM pavedienu plānotājs var ieplānot palaist zemākas prioritātes pavedienu. Pieņemsim, ka dati tiek saņemti, kamēr darbojas zemākas prioritātes pavediens. Lai gan augstākas prioritātes pavedienam vajadzētu darboties, tiklīdz ir saņemti dati, tas nenotiek, kamēr JVM nākamreiz aptaujā operētājsistēmu un atklāj ierašanos. Tādējādi zemākas prioritātes pavediens darbojas, kaut arī augstākas prioritātes pavedienam vajadzētu darboties. Par šo situāciju jāuztraucas tikai tad, ja jums ir nepieciešama Java reāllaika rīcība. Bet tad Java nav reāllaika operētājsistēma, tad kāpēc uztraukties?

Lai saprastu, kurš skrienamais zaļais pavediens kļūst par pašreizējo zaļo pavedienu, apsveriet sekojošo. Pieņemsim, ka jūsu lietojumprogramma sastāv no trim pavedieniem: galvenā pavediena, kas vada galvenais () metode, aprēķina pavediens un pavediens, kas nolasa tastatūras ievadi. Ja nav tastatūras ievades, lasīšanas pavediens tiek bloķēts. Pieņemsim, ka lasīšanas pavedienam ir visaugstākā prioritāte, un aprēķinu pavedienam ir viszemākā prioritāte. (Vienkāršības labad pieņemiet, ka nav pieejami arī citi iekšējie JVM pavedieni.) Šo trīs pavedienu izpildi ilustrē 1. attēls.

Laikā T0 sāk darboties galvenā vītne. Laikā T1 galvenais pavediens sāk aprēķina pavedienu. Tā kā aprēķina pavedienam ir zemāka prioritāte nekā galvenajam pavedienam, aprēķina pavediens gaida procesoru. Laikā T2 galvenais pavediens sāk lasīšanas pavedienu. Tā kā lasīšanas pavedienam ir augstāka prioritāte nekā galvenajam pavedienam, galvenais pavediens gaida procesoru, kamēr lasīšanas pavediens darbojas. Laikā T3 lasīšanas pavediens bloķējas un darbojas galvenā pavediens. Laikā T4 lasīšanas pavediens atbloķē un darbojas; galvenā vītne gaida. Visbeidzot, laikā T5 lasīšanas pavediens bloķējas un galvenais pavediens darbojas. Šī izpildes maiņa starp lasīšanas un galvenajiem pavedieniem turpinās, kamēr programma darbojas. Aprēķina pavediens nekad nedarbojas, jo tam ir viszemākā prioritāte, un tāpēc tas cieš no procesora uzmanības, kas pazīstams kā procesora badošanās.

Mēs varam mainīt šo scenāriju, piešķirot aprēķina pavedienam tādu pašu prioritāti kā galvenajam pavedienam. 2. attēlā parādīts rezultāts, sākot ar laiku T2. (Pirms T2 2. attēls ir identisks 1. attēlam.)

Laikā T2 lasīšanas pavediens darbojas, kamēr galvenie un aprēķina pavedieni gaida procesoru. Laikā T3 lasīšanas pavediens bloķējas un aprēķina pavediens darbojas, jo galvenais pavediens skrēja tieši pirms lasīšanas pavediena. Laikā T4 lasīšanas pavediens atbloķē un darbojas; galvenie un aprēķinu pavedieni gaida. Laikā T5 lasīšanas pavediens bloķējas un galvenais pavediens darbojas, jo aprēķina pavediens ilga tieši pirms lasīšanas pavediena. Šī pārmaiņa izpildē starp galvenajiem un aprēķinu pavedieniem turpinās, kamēr programma darbojas, un ir atkarīga no augstākas prioritātes pavedienu palaišanas un bloķēšanas.

Mums jāņem vērā pēdējais zaļo pavedienu plānošanas elements. Kas notiek, ja zemākas prioritātes pavedienam ir bloķēšana, kas nepieciešama augstākas prioritātes pavedienam? Augstākas prioritātes pavedieni bloķējas, jo tas nevar iegūt bloķēšanu, kas nozīmē, ka augstākas prioritātes pavedienam faktiski ir tāda pati prioritāte kā zemākas prioritātes pavedienam. Piemēram, 6. prioritātes pavediens mēģina iegūt bloķēšanu, kurai pieder 3. prioritātes pavediens. Tā kā 6. prioritātes pavedienam ir jāgaida, līdz tas var iegūt slēdzeni, 6. prioritātes pavediens beidzas ar 3. prioritāti - fenomenu, kas pazīstams kā prioritātes inversija.

Prioritātes inversija var ievērojami aizkavēt augstākas prioritātes pavedienu izpildi. Piemēram, pieņemsim, ka jums ir trīs pavedieni ar prioritātēm 3, 4 un 9. 3. prioritātes pavediens darbojas, un pārējie pavedieni ir bloķēti. Pieņemsim, ka 3. prioritātes pavediens satver bloķēšanu un 4. prioritātes pavediens atbloķē. 4. prioritātes pavediens kļūst par pašlaik darbojošos pavedienu. Tā kā 9. prioritātes pavedienam ir nepieciešama bloķēšana, tas turpina gaidīt, kamēr 3. prioritātes pavediens atbrīvo slēdzeni. Tomēr 3. prioritātes pavediens nevar atbrīvot bloķēšanu, kamēr 4. prioritātes pavediens nav bloķēts vai pārtraukts. Rezultātā 9. prioritātes pavediens aizkavē tā izpildi.

$config[zx-auto] not found$config[zx-overlay] not found