Straumes. Vītņu klase un izpildāmais interfeiss. Klases pavediens Pieklājības pavediens
- Kādas ir pavedienu prioritātes?
Atbilde uz šo jautājumu ir JavaRush lekcijās.
Lai optimizētu Java pavedienu paralēlu darbību, ir iespējams prioritizēt pavedienus. Pavedieniem ar augstāku prioritāti ir priekšrocība iegūt procesora laiku salīdzinājumā ar pavedieniem ar zemāku prioritāti.
Darbu ar prioritātēm nodrošina šādas pavedienu klases metodes:
public final void setPriority (int newPriority)
Iestata pavediena prioritāti.
publiskā fināla int getPriority ()
Ļauj uzzināt pavediena prioritāti.
Parametra vērtība setPriority metodē nevar būt patvaļīga. Tam jābūt no MIN_PRIORITY līdz MAX_PRIORITY. Kad tas ir izveidots, pavedienam ir prioritāte NORM_PRIORITY.
MIN_PRIORITY \u003d 1.
NORM_PRIORITY \u003d 5.
MAX_PRIORITY \u003d 10. - izveidojiet pārāk daudz pavedienu, un resursi tiek tērēti, un arī laiks tiek izšķiests, izveidojot neizmantotus pavedienus
- iznīciniet pārāk daudz pavedienu, un vēlāk būs jāpavada vairāk laika, veidojot tos vēlreiz
- pavedienu izveidošana pārāk lēni var izraisīt sliktu klienta veiktspēju (ilgi jāgaida)
Vai ir iespējams apturēt pavedienu, pazeminot tā prioritāti līdz 0?
Atbilde rakstā: “50 labākie jautājumi intervijā. Tēma: vairāku pavedienu izveidošana »
Atrasts forumā.
Ir šī raksta versija angļu valodā: Top 50 Java pavedienu intervijas jautājumu atbildes svaigajiem, pieredzējušajiem programmētājiem
Java nodrošina bagātīgas API visam, taču, ironiski, tas nenodrošina ērtus pavedienu apturēšanas veidus. JDK 1.0 bija vairākas vadības metodes, piemēram, stop (), aptur () un resume (), kuras tika apzīmētas kā novecojis turpmākajos laidienos iespējamo strupceļa draudu dēļ kopš tā laika Java API izstrādātāji nav mēģinājuši nodrošināt stabilu, drošu un elegantu paņēmienu pavedienu apturēšanai. Programmētāji lielākoties paļaujas uz to, ka pavediens pats apstājas, tiklīdz tas pabeidz izpildes () vai izsaukuma () metodes. Lai apturētu manuāli, programmētāji izmanto nepastāvīgo Būla mainīgo un pārbauda tā vērtību katrā atkārtojumā, ja palaišanas () metodē ir cilpas, vai pārtrauciet pavedienus ar pārtraukt () metodi, lai pēkšņi atceltu darbus.
Konkrēti par šo jautājumu: es nekad neesmu redzējis, ka kāds piešķir prioritāti 0.
Ja kāds kaut ko par šo zina, tad raksti komentāros.
Kāpēc mums ir nepieciešama ThreadGroup klase?
ThreadGroup ir pavedienu kopums, kurā var būt arī citas pavedienu grupas. Diegu grupa veido koku, kurā katrai citai pavedienu grupai ir vecāks (izņemot oriģinālu). Vītnei ir tiesības piekļūt datiem no tās pavedienu grupas, bet tai nav šādas piekļuves citām grupām vai vecāku pavedienu grupai.
No kuras pavedienu grupas sastāv galvenā pavediena grupa?
Es nekur to neesmu atradis)) Sakiet, kur tas atrodas))
Kas ir ThreadPool modelis?
Par to ir izraksts no wikipedia raksta:
Datorprogrammēšanā pavedienu kopas modelis (arī atkārtotie darbinieki vai strādnieku-apkalpes modelis) ir tāds, kurā tiek izveidoti vairāki pavedieni, lai veiktu vairākus uzdevumus, kas parasti tiek organizēti rindā. Izpildīto uzdevumu rezultātus var arī ievietot rindā, vai arī uzdevumi var nesniegt rezultātu (piemēram, ja uzdevums paredzēts animācijai). Parasti ir daudz vairāk uzdevumu nekā pavedieni. Tiklīdz pavediens būs pabeidzis savu uzdevumu, tas prasīs nākamo uzdevumu no rindas, līdz visi uzdevumi būs pabeigti. Pēc tam pavediens var tikt pārtraukts vai gulēt, līdz būs pieejami jauni uzdevumi.
Izmantoto pavedienu skaits ir parametrs, kuru var noregulēt, lai nodrošinātu vislabāko sniegumu. Turklāt pavedienu skaits var būt dinamisks, pamatojoties uz gaidīšanas uzdevumu skaitu. Piemēram, tīmekļa serveris var pievienot pavedienus, ja ienāk daudz tīmekļa lapu pieprasījumu, un var noņemt pavedienus, kad šie pieprasījumi sašaurinās. Lielāka diegu kopuma izmaksas ir palielinātas resursu izmantošanas dēļ. Algoritms, ko izmanto, lai noteiktu, kad izveidot vai iznīcināt pavedienus, ietekmēs kopējo veiktspēju:
Datorprogrammēšanā ir pavedienu kopuma modelis, kurā tiek izveidots noteikts pavedienu skaits, lai veiktu vairākus uzdevumus, kas parasti tiek organizēti rindā. Pabeigto uzdevumu rezultātus var arī ievietot rindā, vai arī uzdevumi var nedot rezultātu (piemēram, ja uzdevums paredzēts animācijai).
Parasti ir daudz vairāk uzdevumu nekā pavedieni. Kad pavediens būs pabeidzis uzdevumu, tas prasīs nākamo uzdevumu no rindas, līdz visi uzdevumi būs pabeigti. Pēc tam plūsma var tikt pārtraukta vai aizmigt. Izmantoto pavedienu skaits ir parametrs, kuru var noregulēt, lai nodrošinātu vislabāko sniegumu. Turklāt pavedienu skaits var būt dinamisks, pamatojoties uz radīto uzdevumu skaitu. Piemēram, tīmekļa serveris var pievienot straumes, ja pienāk pieprasījumi uz vairākām tīmekļa lapām, un var izdzēst straumes, ja ir mazāk pieprasījumu. Palielinoties pavedienu kopuma lielumam, palielinās arī datora resursu izmantošana. Algoritms, ko izmanto, lai noteiktu, kad izveidot vai iznīcināt pavedienus, ietekmēs kopējo veiktspēju: - Pārāk daudz pavedienu nozīmē resursu un laika tērēšanu.
Iznīciniet pārāk daudz pavedienu, un vēlāk to izveidei tiks atvēlēts vairāk laika - pavedienu izveidošana pārāk lēni var pazemināt klienta veiktspēju.
Kāpēc nepieciešama ThreadPoolExecutor klase?
sabiedriskās klases ThreadPoolExecutor paplašina AbstractExecutorService
ExecutorService, kas veic katru iesniegto uzdevumu, izmantojot vienu no vairākiem iespējamiem apvienošanas pavedieniem, kas parasti tiek konfigurēts, izmantojot izpildītāju rūpnīcas metodes.
Vītņu kopas risina divas dažādas problēmas: tās parasti nodrošina uzlabotu sniegumu, veicot lielu daudzumu asinhrono uzdevumu, jo samazināts zvana piemaksa katram uzdevumam, un tie nodrošina līdzekļus ierobežot un pārvaldīt resursus, ieskaitot pavedienus, kas tiek izmantoti uzdevumu kopas izpildē. Katrs ThreadPoolExecutor atbalsta arī dažus statistikas pamatdatus, piemēram, pabeigto uzdevumu skaitu.
Lai šī klase būtu noderīga plašā kontekstā, tā piedāvā daudzus pielāgojamus parametrus un paplašināšanas sviras. Tomēr programmētāji ir pārliecināti, ka izmanto ērtākas Executors rūpnīcas metodes Executors.newCachedThreadPool () (neierobežots pavedienu fonds, ar automātisku pavedienu atkopšanu), Executors.newFixedThreadPool (int) (fiksēta izmēra diegu fonds) un Executors.newSingleThreadExecutor () (viena fona pavediens). kas iepriekš konfigurē iestatījumus visbiežāk izmantotajiem gadījumiem.
Cik veidus jūs zināt, kā izveidot pavedienu?
Valodas līmenī ir divi veidi, kā izveidot pavedienu. Java.lang.Thread klases objekts ir pavediens, taču tam ir nepieciešams izpildes uzdevums, kas ir objekts, kas ievieš java.lang.Runnable saskarni. Tā kā Thread klasē tiek realizēts Runnable interfeiss, varat ignorēt run () metodi, pārmantojot savu klasi no Thread vai tajā ieviešot Runnable interfeisu.
Kāpēc tiek izmantota Future klase?
Nākotne saglabā asinhrona aprēķina rezultātu. Aprēķinu varat sākt, nodrošinot kādam kādu Nākotnes objektu un aizmirstot par to. Objekta Nākotne īpašnieks var iegūt rezultātu, kad tas ir gatavs.
Kādas ir Callable priekšrocības salīdzinājumā ar Runnable?
Callable interfeiss ir daudz piemērotāks paralēlu izpildei paredzētu uzdevumu radīšanai nekā Runnable interfeiss vai Thread klase. Ir vērts atzīmēt, ka spēja pievienot šādu saskarni parādījās tikai kopš Java 5, jo saskarnes Callable galvenā iezīme ir parametrētu tipu (sugas) lietošana, kā parādīts sarakstā.
Sarakstā Uzdevuma izveidošana, izmantojot Callable 10 1 importa javas saskarni. util. vienlaicīga. Izsaucams 11 2 sabiedriskās klases CallableSample ievieš Callable (12 3 publisko virknes zvanu () met izņēmumu (13 4, ja (ir kāds nosacījums) (14 5 mest jauno IOException ( "kļūda uzdevuma apstrādes laikā"); 15 6) 16 7 sistēma. ārā. println ("uzdevums tiek apstrādāts"); 17 8 atgriešanās "rezultāts"; 18 9) 19 10)
Nekavējoties jāpievērš uzmanība 2. rindiņai, kur tiek norādīts, ka Callable saskarne ir parametrizēta, un tās īpašā ieviešana - CallableSample klase ir atkarīga no virknes veida. 3. rinda parāda galvenās zvana metodes parakstu jau parametrētā versijā, jo virknes tips ir norādīts arī kā atgriešanās tips. Faktiski tas nozīmē, ka ir izveidots uzdevums, kura rezultāts būs String tipa objekts (sk. 8. rindu). Tādā pašā veidā jūs varat izveidot uzdevumu, kā rezultātā jebkura veida objekts tiks izveidots un atgriezts zvana veidā. Šāds risinājums ir daudz ērtāks, salīdzinot ar palaišanas metodi saskarnē Runnable, kas neko neatdod (tā atgriešanas tips nav derīgs), un tāpēc uzdevuma rezultāta iegūšanai ir jāizgudro risinājumi.
Vēl viena saskarnes Callable priekšrocība ir spēja "izmest" izņēmumus, neietekmējot citus notiekošos uzdevumus. 3. rinda norāda, ka izņēmums no tipa izņēmuma var tikt “izmests” no metodes, kas faktiski nozīmē jebkuru izņēmumu, jo visi izņēmumi ir java.lang.Exception pēcnācēji. 5. rindā šo funkciju izmanto, lai izveidotu pārbaudītu (pārbaudītu) izņēmumu no tipa IOException. Palaižamās saskarnes palaišanas metode vispār neļāva mest kontrolētus izņēmumus, un nekontrolēta (izpildlaika) izņēmuma mešana lika pavedienam un visai lietojumprogrammai apstāties.
Vai ir iespējams atcelt uzdevumu, ja izmantojat nākotnes klasi?
Balstoties uz šo diskusiju, kas izvirzīta centrā, izrādās, ka tas nav iespējams.
Future ir metode Future.cancel (Būla), kurai vajadzētu atcelt uzdevuma izpildi. Bet, ja uzdevums jau ir sācies, izsaucot Future.cancel (true), tas to patiešām neapturēs. FutureTask ieviešanas versijās kods tiek izpildīts:
if (mayInterruptIfRunning) (Vītne r \u003d runner; if (r! \u003d null) r. pārtraukt ();)Tie. atkal pavediens, kurā darbojas uzdevums, ir ieteicams tikai pārtraukt izpildi. Turklāt mums pat nav iespējas noskaidrot, vai uzdevums šobrīd tiek veikts vai nē. Ir metode Future.isDone (), taču atkal tā atgriežas taisnība ne tikai tad, kad uzdevums pabeidza izpildi, bet tūlīt pēc izsaukšanas uz Future.cancel (), pat ja uzdevums joprojām darbojas (galu galā Future.cancel (taisnība) neaptur uzdevumu, kas jau sācis darboties).
Ja mēs visu kodu uzrakstīsim paši, tad jūs varat rūpīgi apstrādāt Thread.isInterrupted () pareizajās vietās, un viss būs kārtībā. Bet ja mēs darbinām trešās puses kodu? Vai mums vajadzētu būt paplašināmam serverim ar spraudņiem? Jebkurš šķībi uzrakstīts spraudnis var viegli novest pie visa servera nedarbojas, jo mēs nevaram pareizi pārtraukt iesaldēta spraudņa izpildi.
Krievu terminoloģijā aiz termina Vītne Pastiprināts tulkojums “Straume”. Lai gan šo vārdu var tulkot arī kā “pavedienu”. Dažreiz ārvalstu mācību materiālos plūsmas jēdziens tiek precīzi izskaidrots pavedienos. Mēs turpinām loģisko sēriju - kur ir pavedieni, tur ir bumba. Un kur ir bumba, tur ir kaķis. Ir uzreiz skaidrs, ka tulkotājiem nebija kaķu. Tā radās apjukums. Turklāt šim terminam ir arī citi pavedieni Straume. Tulkotāji parasti ir dīvaini cilvēki.
Kad jebkura programma tiek startēta, tiek saukts pavediens galvenā straume (galvenais). No tā tiek ģenerētas bērnu straumes. Galvenais pavediens parasti ir pēdējais pavediens programmas pārtraukšanai.
Neskatoties uz to, ka galvenais pavediens tiek izveidots automātiski, to var vadīt, izmantojot klases objektu Vītne. Lai to izdarītu, piezvaniet uz metodi currentThread (), pēc kura jūs varat kontrolēt plūsmu.
Klase Vītne satur vairākas plūsmu pārvaldības metodes.
- getName () - iegūstiet straumes vārdu
- getPriority () iegūt pavediena prioritāti
- ir dzīvs () - noteikt, vai pavediens darbojas
- pievienoties () - pagaidiet straumes pabeigšanu
- palaist () - sākt straumi. Ierakstiet tajā savu kodu
- gulēt () - noteiktā laikā apturēt straumi
- sākt () - sākt straumi
Mēs iegūsim informāciju par galveno pavedienu un mainīsim tā nosaukumu.
Pavediens mainThread \u003d Thread.currentThread (); mInfoTextView.setText ("Pašreizējais pavediens:" + mainThread.getName ()); // mainiet vārdu un displeju teksta laukā mainThread.setName ("CatThread"); mInfoTextView.append ("\\ nJauna pavediena nosaukums:" + mainThread.getName ());
Galvenā pavediena noklusējuma nosaukums galvenaiskuru mēs aizstājām ar Catthread.
Mēs iegūsim informāciju par straumes nosaukumu, nenorādot metodi.
Pavediens mainThread \u003d Thread.currentThread (); mInfoTextView.setText ("Pašreizējais pavediens:" + mainThread);
Šajā gadījumā jūs varat redzēt līniju Vītne - straumes nosaukums, tās prioritāte un grupas nosaukums.
Izveidojiet savu straumi
Izveidot savu straumi nav grūti. Pietiek ar mantošanu no klases Vītne.
Mēs deklarējam iekšējo klasi mūsu klases iekšienē un izsaucam to, noklikšķinot, izsaucot metodi sākt ().
Sabiedriskā klase MyThread paplašina pavedienu (public void run () (Log.d (TAG, "My thread are running ...");)) public void onClick (View view) (MyThread myThread \u003d new MyThread (); myThread.start ( );)
Alternatīvi, pārsūtiet metodes izsaukumu sākt () pie konstruktora.
Publisks voClick (skata skats) (MyThread myThread \u003d new MyThread ();) publiskās klases MyThread paplašina pavedienu (// Constructor MyThread () (//) Izveidot jaunu pavedienu super ("Otrais pavediens"); Log.i (TAG, " Tiek izveidota otrā straume "+ this); start (); // Start the stream) public void run () (Log.d (TAG," Mana straume darbojas ... "); try (for (int i \u003d 5; i\u003e 0; i--) (Log.i (TAG, "Otrais pavediens:" + i); Thread.sleep (500);)) nozveja (InterruptedException e) (Log.i (TAG, "Otrais pavediens pārtraukts"); )))
Palaišanas pavediena izveidošana
Straumes izveidošanai ir sarežģītāka iespēja. Lai izveidotu jaunu pavedienu, jums jāīsteno interfeiss Palaižams. Straumi var izveidot no jebkura objekta, kas ievieš saskarni. Palaižams un pasludina metodi palaist ().
Iekšējā metode palaist () jūs ievietojat koda jaunu pavedienu. Šis pavediens beigsies, kad metode atgriezīsies.
Kad jūs deklarējat jaunu klasi ar interfeisu Palaižamsjums jāizmanto konstruktors:
Vītne (izpildāms pavediena_objekts, stīgas pavediena nosaukums)
Pirmais parametrs norāda klases eksemplāru, kas ievieš saskarni. Tas nosaka, kur sāksies pavediena izpilde. Otrais parametrs nodod straumes nosaukumu.
Pēc jauna pavediena izveidošanas jums tas jāuzsāk, izmantojot metodi sākt (), kas būtībā izsauc metodes izsaukumu palaist ().
Mācību projektā kā ligzdotu klasi izveidojiet jaunu pavedienu un palaidiet to.
Komplekts en.alexanderklimov.expresscourse; importa android.os.Bundle; importēt android.support.v7.app.AppCompatActivity; importa android.util.Log; importa android.view.View; importēt android.widget.Button; importēt android.widget.EditText; importēt android.widget.TextView; importēt statisku ru.alexanderklimov.expresscourse.R.id.textViewInfo; publiskās klases MainActivity paplašina AppCompatActivity (galīgā virknes TAG \u003d "ExpressCourse"; privāta poga mButton; privāta EditText mResultEditText; privāta TextView mInfoTextView; @Override aizsargāta tukšums onCreate (Bundle savedInstanceState) (super.onCreate (savedInstanceState). ); mButton \u003d (Poga) findViewById (R.id.buttonGetResult); mResultEditText \u003d (EditText) findViewById (R.id.editText); mInfoTextView \u003d (TextView) findViewById (textViewidfo); MyRunnable (); // izveidojiet jaunu pavedienu mēģinājumu (for (int i \u003d 5; i\u003e 0; i--)) (Log.i (TAG, "Galvenā vītne:" + i); Thread.sleep (1000);) ) noķeršana (InterruptedException e) (Log.i (TAG, "Galvenā vītne pārtraukta");)) MyRunnable klases darbarīki Runnable (Threads thread; // Constructor MyRunnable () (// Izveidot jaunu otrā diega pavedienu \u003d new Thread (this, "Vītne, piemēram"); \u200b\u200bLog.i (TAG, "Otrais pavediens izveidots" + pavediens); thread.start (); // Palaist p otok) // Nepieciešamā metode Runnable interfeisa public void run () (mēģiniet (for (int i \u003d 5; i\u003e 0; i--) (Log.i (TAG, "Otrais pavediens:" + i); Thread.sleep (500);)) nozveja (InterruptedException e) (Log.i (TAG, "Otrais pavediens pārtraukts");)) )))
Iekšpusē konstruktors MyRunnable () mēs izveidojam jaunu klases objektu Vītne
Vītne \u003d jauns pavediens (tas, piemēram, "Vītne");
Pirmais parametrs izmantoja objektu šo, kas nozīmē vēlmi izsaukt metodi palaist () šis objekts. Tālāk tiek saukta metode. sākt (), kā rezultātā tiek sākta pavediena izpilde, sākot ar metodi palaist (). Savukārt metode sāk cilpu mūsu pavedienam. Pēc metodes izsaukšanas sākt ()konstruktors MyRunnable () atdod kontroli lietojumprogrammai. Kad galvenais pavediens turpina darbu, tas nonāk savā ciklā. Pēc tam abi pavedieni tiek izpildīti paralēli.
Papildus pirmajam varat palaist vairākus pavedienus, ne tikai otro pavedienu. Tas var radīt problēmas, ja divi pavedieni mēģina strādāt ar vienu un to pašu mainīgo vienlaikus.
Sinhronizēts atslēgas vārds - sinhronizētās metodes
Lai atrisinātu problēmu ar pavedieniem, kas var radīt neskaidrības, tiek izmantota sinhronizācija.
Metodei var būt modifikators. sinhronizēts. Kad pavediens atrodas sinhronizētā metodē, jāgaida visi citi pavedieni, kas to mēģina izsaukt vienā un tajā pašā gadījumā. Tas novērš neskaidrības, kad vairāki pavedieni mēģina izmantot metodi.
Sinhronizēts tukšs meow (stīgas ziņojums);
Turklāt atslēgvārds sinhronizēts var izmantot kā operatoru. Jūs varat ievietot blokā sinhronizēts dažu klašu metodes izsaukumi:
Sinhronizēts (objekts) (// paziņojumi, kuriem nepieciešama sinhronizācija)
Looper
Straumē ir entītijas Looper, Apstrādātājs, MessageQueue.
Katram pavedienam ir viens unikāls Looper un to var būt daudz Apstrādātājs.
Grāfs Looper papildu straumes objekts, kas to kontrolē. Tas apstrādā ienākošos ziņojumus un arī uzdod pavedienam pārtraukt pareizā laikā.
Straume kļūst tā Looper un MessageQueue izmantojot metodi Looper.prepare () pēc palaišanas. Looper.prepare () identificē izsaucēja pavedienu, izveido Looper un MessageQueue un saista straumi ar tiem krātuvē Threadlocal. Metode Looper.loop () jāaicina skriet Looper. Jūs varat pabeigt tā darbu, izmantojot metodi looper.quit ().
Klases LooperThread paplašina pavedienu (public Handler mHandler; public void run () (Looper.prepare (); mHandler \u003d new Handler () (public void handleMessage (Message msg) (// šeit apstrādā ienākošos ziņojumus)); Looper.loop () ;))
Izmantojiet statisko metodi getMainLooper () piekļūt Looper galvenais pavediens:
Looper mainLooper \u003d Looper.getMainLooper ();
Izveidojiet divus pavedienus. Mēs sāksim vienu galvenajā plūsmā, bet otro atsevišķi no galvenā. Mums pietiks ar divām pogām un etiķeti.
Ievērojiet, kā sākas pavedieni. Pirmais pavediens tiek sākts, izmantojot metodi sākt (), un otrais - palaist (). Tad mēs pārbaudām, kurā straumē mēs atrodamies.
Komplekts ru.alexanderklimov.as23; importa android.os.Bundle; importa android.os.Looper; importēt android.support.v7.app.AppCompatActivity; importa android.view.View; importēt android.widget.Button; importēt android.widget.TextView; Sabiedriskās klases MainActivity paplašina AppCompatActivity (TextView mInfoTextView; @Orride aizsargāts void onCreate (Bundle savedInstanceState) (super.onCreate (savedInstanceState); setContentView (R.layout.activity_main); Button startButton \u003d (Button) findViewByrtI) Poga runButton \u003d (poga) findViewById (R.id.button_run); mInfoTextView \u003d (TextView) findViewById (R.id.textview_info); startButton.setOnClickListener (jauns skats.OnClickListener () (@Override public void) Pavediena pavediens \u003d jauns pavediens (jauns MyRunnable ()); thread.start (); // fona pavedienā))); runButton.setOnClickListener (jauns View.OnClickListener ()) (@Override public void onClick (View view)) \u003d jauns pavediens (jauns MyRunnable ()); thread.run (); // pašreizējā pavedienā)));) privātā klase MyRunnable īsteno Runnable (@Override public void run () (// Pārbaudiet, kurā pavedienā mēs esam, ja (( Looper.getMainLooper (). GetThread () \u003d\u003d Thread.currentThread ()) (mInfoText View.setText ("Galvenajā straumē"); ) else (runOnUiThread (new Runnable () (@Override public void run () (mInfoTextView.setText ("Fona pavedienā";)));))))))
Šī tēma ir diezgan sarežģīta, un lielākajai daļai tas neinteresē un nav jāpēta.
Operētājsistēmā Android tīras straumes tiek izmantotas arvien mazāk, sistēmai ir savi veidi.
Kādos stāvokļos diegs var iekļūt, ievadot sinhronizētu bloku?
- RUNNABLE
- BLOKĒTS
RUNNABLE gadījumā, ja koda bloks, kas apzīmēts sinhronizēts, nav aizņemts ar citu pavedienu. Pretējā gadījumā mūsu pavediens saņems BLOKĒTU stāvokli un gaidīs, kamēr mutex objekts tiks atbrīvots.
Izsaucot šo metodi, pavediens tiek gaidīts.
Gaidīšanas () metodi var izsaukt tikai mutex objekta sinhronizētajā blokā, kuru pašreizējā vītne ir "bloķējusi (bloķējusi)", pretējā gadījumā metode radīs izņēmumu IllegalMonitorStateException.
Objekta monitors \u003d getMonitor (); sinhronizēts (monitors) (... monitors. pagaidiet (); ...)
Kad tiek izsaukta gaidīšanas () metode, pašreizējais pavediens atbloķē monitora objektu un nonāk Gaidīšanas stāvoklī, gaidot, līdz monitor.notify () vai monitor.notifyAll () metodi izsauks cits pavediens. Tiklīdz tas notiks, pavediens pamodīsies un, ja monitors nebija aizņemts, tad tas to uztvers un turpinās strādāt.
Kādā stāvoklī pavediens nonāk, kad tiek izsaukta gaidīšanas (500) metode?
Izsaucot šo metodi, pavediens tiek mainīts uz TIMED_WAITING stāvokli.
Pēc analoģijas ar gaidīšanas () metodi gaidīšanu (noildzi) var izsaukt tikai mutex objekta sinhronizētajā blokā, kuru pašreizējais pavediens ir "aizslēdzis (bloķējis)". Objekta monitors \u003d getMonitor (); sinhronizēts (monitors) (... monitors. pagaidiet (500); ...)
Kad tiek izsaukta gaidīšanas () metode, pašreizējā vītne atslēdz monitora objektu un aizmieg 500 milisekundēs. Monitora objektu var uztvert ar citu pavedienu.
Pēc 500 milisekundēm pavediens pamostas un, ja monitors nebija aizņemts, tad tas to uztvers un turpinās strādāt.
Ja monitors aizņemts ar citu pavedienu, pašreizējais pavediens nonāk BLOCKED stāvoklī.
Kādā stāvoklī pavediens nonāks, kad tiks izsaukta paziņošanas () metode?
Objekta monitors \u003d getMonitor (); sinhronizēts (monitors) (... monitors. pagaidiet (); ...)
Pēc monitor.wait () pavediens nonāk Gaidīšanas stāvoklī. Paziņošanas () metode, ko izsauc cits monitora objekta pavediens, pavedienu pārcels no Gaidīšanas stāvokļa uz RUNNABLE stāvokli, ja monitora objektu neuztver cits pavediens, pretējā gadījumā - BLOKĒTS.
Kurā stāvoklī pavediens nonāk, kad tiek izsaukta metodeAntAll ()?
NotifyAll () "pamostas" visi pavedieni. Viens no Gaidīšanas pavedieniem nonāks RUNNABLE stāvoklī, uztver izmantotā objekta monitoru un turpinās savu darbu. Pārējais būs bloķēts. Tiklīdz pirmais “pamodinātais” pavediens atlaidīs monitoru, kuru visi citi gaida, nākamais pavediens atkārtos tā likteni (patvaļīgs pavediens no BLOCKED stāvokļa nonāks RUNNABLE stāvoklī). Tas turpināsies, līdz visi pamodinātie pavedieni pamet bloķēto stāvokli.
Trīs sinhronizētā bloka pavedieni, kurus mutex objektā sauc par wait (). Uz kādu stāvokli šie pavedieni attieksies, ja ceturtais pavedienu izsaukums értesAll ()?
Divas no tām nonāks bloķētā stāvoklī, viena - RUNNABLE stāvoklī
Kāda ir atšķirība starp pievienošanos (500) un gaidīšanu (500)?
Neskatoties uz to, ka abi pievienojas (500) un gaida (500), pašreizējais pavediens tiek ievietots TIMED_WAITING stāvoklī, starp tiem pastāv būtiskas atšķirības:
savienojums (500) tiek saukts pavedienā, gaidīšana (500) tiek izsaukta tā objekta sinhronizētajā blokā, kurā tiek sinhronizēts dotais bloks.
Kad tiek izsaukts savienojums (500), pašreizējais pavediens gaidīs 500 milisekundes, lai pabeigtu pavedienu, kura savienojuma () metode tika izsaukta.
Pēc 500 milisekundēm abos gadījumos pavedieni turpina darboties.
Kāda ir atšķirība starp gaidīšanu (500) un miegu (500)?
Miega režīms (500) tiek saukts pavedienā, gaidīšana (500) tiek izsaukts tā objekta sinhronizētajā blokā, kurā šis bloks tiek sinhronizēts.
Zvanot miega režīmam (500), pašreizējais pavediens gaidīs 500 milisekundes, pēc tam turpinās darbu.
Kad tiek izsaukts gaidīšana (500), pašreizējais pavediens atslēdz sinhronizēto objektu un aizmieg uz 500 milisekundēm.
Kādā stāvoklī pavediens nonāk, kad tiek saukta ražas () metode?
Kad tiek saukta ražas () metode, pašreizējais pavediens “izlaiž apgriezienus” un java nekavējoties pārslēdzas uz nākamo pavedienu. Vītne no darba stāvokļa nonāk gatavības stāvoklī. Darbības un gatavības stāvokļi ir RUNNABLE stāvokļa apakšstāvokļi.
Vītņu klase
Pavedienu klase ir visvienkāršākā no visām System.Threads nosaukumvietas tipiem. Šī klase apzīmē uz objektu orientētu aptinumu ap doto izpildes ceļu noteiktā AppDomain. Šis tips nosaka arī metožu kopumu (gan statiskā, gan instanču līmenī), kas ļauj pašreizējā AppDomain izveidot jaunus pavedienus, kā arī apturēt, apturēt un iznīcināt noteiktu pavedienu. Turpmāk sniegts galveno statisko dalībnieku saraksts:
Pašreizējais kontekstsŠis tikai lasāmais īpašums atgriež kontekstu, kurā pavediens pašlaik darbojas.
Pašreizējā vītneŠis tikai lasāmais īpašums atgriež atsauci uz pašreizējo pavedienu.
GetDomain (), GetDomainID () Gulēt ()Šī metode uz noteiktu laiku aptur pašreizējo pavedienu.
Pavedienu klase atbalsta arī vairākas instanču līmeņa metodes, no kurām dažas ir aprakstītas tabulā zemāk. Aktīva pavediena atcelšana vai apturēšana parasti tiek uzskatīta par sliktu ideju. Kad jūs to darāt, pastāv iespēja (kaut arī maza), ka pavediens var noplūst darba slodzē, kad tas tiek traucēts vai pārtraukts.
Instances līmeņa loceklis | Pieraksts |
Ir dzīvs | Atgriež Būla vērtību, norādot, vai pavediens darbojas (un vēl nav pārtraukts vai atcelts) |
Isbackground | Iegūst vai iestata vērtību, norādot, vai šī straume ir "fona" (sīkāk paskaidrots zemāk) |
Vārds | Ļauj iestatīt draudzīgu teksta straumes vārdu |
Prioritāte | Iegūst vai iestata pavediena prioritāti, kurai var būt vērtība no ThreadPriority uzskaitījuma. |
Threadstate | Iegūst šī pavediena stāvokli, kuram var piešķirt vērtību no ThreadState uzskaitījuma. |
Pārtraukt () | Uzdod CLR pēc iespējas ātrāk pārtraukt pavedienu |
Pārtraukt () | Pārtrauc (t.i., pārtrauc) pašreizējo pavedienu uz noteiktu gaidīšanas periodu |
Pievienoties () | Bloķē izsaucamo pavedienu, līdz beidzas norādītais pavediens (tas, kurā tiek izsaukts Join ()) |
Turpināt () | Atsāk iepriekš apturētu straumi |
Sākt () | Uzdod CLR pēc iespējas ātrāk sākt pavedienu |
Apturēt () | Aptur pavedienu. Ja pavediens jau ir apturēts, izsaukšanai Apturēt () nav nozīmes |
Statistikas datu iegūšana par pašreizējo pavedienu
Atgādiniet, ka izpildāmās montāžas (t.i., metodes Main ()) ieejas punkts darbojas primārajā izpildes pavedienā. Lai ilustrētu Thread veida pamata lietojumu, pieņemsim, ka jums ir jauna konsoles lietojumprogramma. Kā jūs zināt, statiskā īpašība Thread.CurrentThread izgūst objektu pavedienu, kas apzīmē pašreizējo pavedienu. Pēc pašreizējās straumes saņemšanas varat parādīt dažādu statistiku par to:
Izmantojot sistēmu; izmantojot System.Collections.Generic; izmantojot System.Linq; izmantojot System.Text; izmantojot System.Threading; namespace ConsoleApplication1 (klase Programma (statisks tukšums Main () (Console.Title \u003d "(! LANG: Informācija par galveno programmas straumi)"; Thread thread = Thread.CurrentThread; thread.Name = "MyThread"; Console.WriteLine(@"Имя домена приложения: {0} ID контекста: {1} Имя потока: {2} Запущен ли поток? {3} Приоритет потока: {4} Состояние потока: {5}", Thread.GetDomain().FriendlyName, Thread.CurrentContext.ContextID, thread.Name, thread.IsAlive, thread.Priority, thread.ThreadState); Console.ReadLine(); } } }!}
Kaut arī šis kods ir vairāk vai mazāk acīmredzams, ņemiet vērā, ka pavedienu klase atbalsta rekvizītu ar nosaukumu Name. Ja precīzi nenosakāt tā vērtību, tad nosaukums atgriezīs tukšu virkni. Piešķirot draudzīgu vārdu konkrētam pavediena objektam, tas var ievērojami vienkāršot atkļūdošanu. Atkļūdošanas sesijas laikā Visual Studio 2010 varat atvērt pavedienu logu, atlasot izvēlnes vienumu Debug -\u003e Windows -\u003e Threads (Atkļūdošana -\u003e Windows -\u003e Threads).
Ņemiet vērā, ka īpašums ar nosaukumu Prioritāte ir definēts pavediena tipā. Pēc noklusējuma visiem pavedieniem ir Normal prioritātes līmenis. Tomēr to var mainīt jebkurā pavediena dzīves ciklā, izmantojot īpašību Thread.Priority un ar to saistīto System.Threading.ThreadPriority uzskaitījumu:
Publiskais enume ThreadPriority (Zemākā, ZemākNormāla, Parasta, // Noklusējuma vērtība. VirsNormāla, Augstākā)
Iestatot pavediena prioritātes līmeni uz vērtību, kas nav noklusējuma vērtība (ThreadPriority.Normal), jāpatur prātā, ka tas nenodrošina tiešu kontroli pār to, kā pavedienu plānotājs pārslēgs pavedienus savā starpā. Faktiski pavediena prioritātes līmenis sniedz CLR mājienu par pavediena darbību nozīmīgumu. Tādējādi pavediens ar ThreadPriority.Augstākajam prioritātes līmenim nav obligāti jāgarantē augstākās prioritātes saņemšana.
Pieņemsim, ka jūs rakstāt cauruļvadu, kurā 2 pavedieni apstrādā datus, izmantojot koplietotu buferi. Ražotāju straume izveido šos datus, un patērētāja straume tos apstrādā (Ražotāja - patērētāja problēma). Šis kods ir visvienkāršākais modelis: izmantojot std :: pavedienu, mēs izveidojam patērētāju straumi un mēs izveidosim datus galvenajā straumē.
Void product () (// izveidojiet uzdevumu un ievietojiet to rindā) void consume () (// nolasiet datus no rindas un apstrādājiet to) int main (int, char **) (std: diegu thr (patērēt); // ģenerēt) straume producēt (); // izveidot datus apstrādei thr.join (); // gaidīt, kamēr funkcija darbosies patērēt () atgriezt 0;)
Mēs izlaižam abu pavedienu sinhronizācijas mehānismus un pievēršam uzmanību galvenajai () funkcijai. Mēģiniet uzminēt, kas ar šo kodu ir nepareizs, un kā to labot?
Teiksim, ka funkcija patērēt () rada izņēmumu. Tā kā šis izņēmums tiek izmests bērna pavedienā, jūs to nevarat noķert un apstrādāt galvenajā pavedienā. Ja bērna pavediena kaudzes paplašināšanas laikā nebija piemērota izņēmuma apstrādātāja, tiks izsaukta funkcija std :: terminate (), kas pēc noklusējuma izsauks funkciju pārtraukt (). Citiem vārdiem sakot, ja jūs nerīkosities ar izņēmumu pavedienā, ko izveidojis thr objekts, programma izies ar kļūdu.
Funkcijas producēšana () izmantošana ir nedaudz sarežģītāka. Pieņemsim, ka šī funkcija rada izņēmumu. Pirmais, ko vēlaties darīt, ir ietīt galveno () korpusu izmēģināšanas blokā:
Izmēģiniet (std :: diegi thr (patērēt); ražot (); // met izņēmumu thr.join ();) nozveja (...) ()
Liekas, ka problēma ir atrisināta, taču, mēģinot palaist šo kodu, programma tomēr sabruks. Kāpēc tas notiek? Pareizosim.
std :: pavediens
Kā jūs, iespējams, uzminējāt, problēma nav saistīta ar cauruļvadu, bet principā ar pareizu standarta bibliotēkas izpildes pavedienu izmantošanu. Jo īpaši šāda vispārināta funkcija ir līdzvērtīga, un tai ir tādas pašas problēmas:
Tukšs brauciens (funkcija
Pirms pāriet pie mūsu problēmas risināšanas, īsi atcerēsimies, kā darbojas std :: pavediens.
1) inicializācijas konstruktors:
Veidne
Kad tiek inicializēts objekts std :: pavediens, tiek izveidots jauns pavediens, kurā tiek palaista fn funkcija ar iespējamiem args argumentiem. Veiksmīgi izveidojot, noteikts objekta piemērs sāk attēlot šo straumi vecāku straumē, un objekta īpašībās tiek iestatīts savienojamais karodziņš.
Atcerieties: savienojams ~ objekts ir saistīts ar straumi.
2) Mēs gaidām ģenerētā pavediena izpildes beigas:
Nederīgs pavediens :: join ();
Šī metode bloķē turpmāku mātes pavediena izpildi, līdz bērns to pabeidz. Pēc veiksmīgas izpildes straumes objekts pārstāj to pārstāvēt, jo mūsu straume vairs nepastāv. Pievienojamais karodziņš tiek izmests.
3) Nekavējoties “atvienojiet” objektu no straumes:
Tukšs pavediens :: atdalīt ();
Šī nav bloķējoša metode. Pievienojamais karodziņš tiek atiestatīts, un bērna pavediens tiek atstāts pašu ierīcēs un darbu pabeigs dažkārt vēlāk.
4) iznīcinātājs:
Vītne :: ~ pavediens ();
Iznīcinātājs iznīcina priekšmetu. Turklāt, ja šim objektam ir pievienojamais karodziņš, tiek izsaukta funkcija std :: terminate (), kas pēc noklusējuma izsauks funkciju pārtraukt ().
Uzmanību! Ja mēs izveidojām objektu un pavedienu, bet neaicinājām pievienoties vai atdalīties, tad programma sabruks. Principā tas ir loģiski - ja objekts joprojām ir savienots ar straumi, tad jums ar to kaut kas jādara. Vēl labāk, nedariet neko un beidziet programmu (vismaz tā nolēma standarta komiteja).
Tāpēc, kad funkcijā producēt () rodas izņēmums, mēs cenšamies iznīcināt savienojamo objektu.
Ierobežojumi
Kāpēc standarta komiteja nolēma rīkoties tā, nevis savādāk? Vai nebūtu labāk izsaukt join () vai irrch () instructor? Izrādās, ka nav labāk. Apskatīsim abus šos gadījumus.
Pieņemsim, ka mums ir klase joining_thread, kas izsauc join () tās iznīcinātājā:
Pievienojies_divai :: ~ pievienojies_daļai () (pievienojies ();)
Pēc tam, pirms rīkoties ar izņēmumu, mums būs jāgaida, kamēr bērna pavediens beigs darboties, jo join () bloķē turpmāku programmas izpildi. Un ja tā notika, ka radītā straume nonāca bezgalīgā cilpā?
Nederīgs patēriņš () (kamēr (1) (...)) ... mēģiniet (joining_thread thr (patērēt); mest std :: izņēmums ();) noķert (...) (// drīz var nenotikt, vai nekad)
Nu, mēs uzzinājām, ka labāk nav aicināt join () iznīcinātājā (kamēr neesat pārliecināts, ka tā ir pareiza notikumu apstrāde), jo šī ir bloķējoša darbība. Kā ir ar atslāņošanos ()? Kāpēc neizsaukt šo nebloķēšanas metodi iznīcinātājā, ļaujot galvenajam pavedienam turpināt darbu? Pieņemsim, ka mums ir šāda klase detaching_thread.
Bet tad mēs varam nonākt šādā situācijā, kad ģenerētais pavediens mēģina izmantot resursu, kura tur vairs nav, piemēram, šādā situācijā:
Izmēģiniet (int dati; atdalot_vītni th (patērēt, & dati); // šajā gadījumā patērē rādītāju uz int kā argumentu mest std :: poikkejums ()) noķert (...) (// pareizi rīkoties ar izņēmumu // patērēt turpina izpildīts, bet attiecas uz jau izdzēstu datu objektu)
Tādējādi standarta veidotāji nolēma novirzīt atbildību uz programmētāju - galu galā viņš labāk zina, kā programmai vajadzētu rīkoties šādos gadījumos. Balstoties uz visu to, izrādās, ka standarta bibliotēka ir pretrunā ar principu - veidojot std :: pavedienu, mums pašiem ir jārūpējas par pareizu resursu pārvaldību, tas ir, skaidri izsaukt pievienoties vai atdalīties. Šī iemesla dēļ daži programmētāji neiesaka izmantot std :: diegu objektus. Tāpat kā jauns un dzēsts, std :: pavediens nodrošina iespēju uz tiem balstīt augstākas pakāpes rīkus.
Lēmums
Viens no šādiem rīkiem ir klase no Boost boost :: thread_joiner bibliotēkas. Tas atbilst mūsu savienojošajam pavedienam iepriekšminētajā piemērā. Ja darbam ar straumēm varat atļauties izmantot trešo personu bibliotēkas, tad labāk to darīt.