Srautai. Gijų klasė ir „Runnable“ sąsaja. Klasės gija Mandagumo gija
- Kokie yra gijų prioritetai?
Atsakymas į šį klausimą yra „JavaRush“ paskaitose.
Norėdami optimizuoti „Java“ gijų lygiagretų veikimą, galima teikti prioritetą gijoms. Gijos, turinčios didesnį prioritetą, turi pranašumą, nes gauna procesoriaus laiką, palyginti su gijomis, kurių prioritetas mažesnis.
Darbą su prioritetais teikia šie temų klasės metodai:
public public void setPriority (int naujoPriority)
Nustato sriegio prioritetą.
viešas final int getPriority ()
Leidžia žinoti gijos prioritetą.
Parametro reikšmė naudojant „setPriority“ metodą negali būti savavališka. Jis turi būti nuo MIN_PRIORITY iki MAX_PRIORITY. Kai jis bus sukurtas, gija turi pirmenybę NORM_PRIORITY.
MIN_PRIORITY \u003d 1.
NORM_PRIORITY \u003d 5.
MAX_PRIORITY \u003d 10. - sukurkite per daug gijų, švaistomi ištekliai ir laikas, taip pat švaistomas nenaudojamų gijų kūrimas
- sunaikinkite per daug gijų ir daugiau laiko praleisite vėliau, kurdami jas iš naujo
- sukurdami temas per lėtai, klientas gali prastai veikti (ilgai laukti)
Ar įmanoma sustabdyti siūlą sumažinus jo prioritetą iki 0?
Straipsnio atsakymas: „50 geriausių klausimų interviu metu. Tema: daugiasluoksnė »
Rasta forume.
Yra šio straipsnio versija anglų kalba: 50 geriausių „Java“ temų interviu klausimų atsakymai gaivintojams, patyrusiems programuotojams
„Java“ teikia turtingas API viskam, tačiau, keista, tačiau ji nepateikia patogių būdų, kaip sustabdyti giją. „JDK 1.0“ buvo keli kontrolės metodai, tokie kaip „stop“ (), sustabdyti () ir atnaujinti (), kurie buvo pažymėti kaip nebenaudojamas būsimose versijose dėl galimo aklavietės grėsmių, nuo to laiko „Java API“ kūrėjai nemėgino pateikti patikimo, saugaus ir elegantiško gijų sustabdymo būdo. Programuotojai dažniausiai remiasi tuo, kad gija sustoja pati, kai tik baigia vykdyti run () arba call () metodus. Norėdami sustabdyti rankiniu būdu, programuotojai pasinaudoja nepastoviu boolean kintamuoju ir patikrina jo vertę kiekvienoje iteracijoje, jei „run ()“ metode yra kilpų, arba pertraukite gijas su pertraukimo () metodu, kad staiga atšauktumėte užduotis.
Konkrečiai šiuo klausimu: Aš niekada nemačiau, kad kas nors teiktų pirmenybę 0.
Jei kas nors apie tai žino, rašykite komentaruose.
Kodėl mums reikia „ThreadGroup“ klasės?
„ThreadGroup“ yra gijų rinkinys, kuriame taip pat gali būti kitų gijų grupių. Gijų grupė sudaro medį, kuriame kiekviena gijų grupė turi tėvystę (išskyrus originalą). Gija turi teisę pasiekti duomenis iš savo gijų grupės, tačiau neturi tokios prieigos prie kitų grupių ar pagrindinės gijų grupės.
Iš kurios gijų grupės susideda pagrindinis gija?
Niekur neradau)) Pasakyk man, kur ji yra))
Kas yra „ThreadPool“ modelis?
Yra ištrauka iš vikipedijos straipsnio apie tai:
Kompiuterių programavimo metu siūlų telkinio schema (taip pat pakartojami darbuotojai arba darbuotojo ir įgulos modelis) yra tokia, kai sukuriama daugybė gijų, kad būtų galima atlikti daugybę užduočių, kurios paprastai organizuojamos eilėje. Vykdomų užduočių rezultatai taip pat gali būti dedami į eilę, arba gali būti, kad užduotys neduos jokio rezultato (pavyzdžiui, jei užduotis skirta animacijai). Paprastai užduočių yra daug daugiau nei gijų. Kai tik gija baigs savo užduotį, ji pareikalaus iš eilės atlikti kitą užduotį, kol visos užduotys bus baigtos. Gija gali nutrūkti arba miegoti, kol bus naujų užduočių.
Naudojamų gijų skaičius yra parametras, kurį galima suderinti, kad būtų užtikrintas geriausias našumas. Be to, siūlų skaičius gali būti dinamiškas, atsižvelgiant į laukiančių užduočių skaičių. Pvz., Žiniatinklio serveris gali pridėti gijų, jei pateikiama daugybė tinklalapio užklausų, ir gali pašalinti gijas, kai tos užklausos nusileidžia žemyn. Didesnio siūlų fondo kaina padidina išteklių naudojimą. Algoritmas, naudojamas nustatyti, kada kurti ar sunaikinti gijas, turės įtakos bendram našumui:
Kompiuteriniame programavime yra siūlų telkinio modelis, kuriame sukuriamas tam tikras skaičius gijų, kad būtų galima atlikti daugybę užduočių, kurios paprastai yra išdėstytos eilėje. Atliktų užduočių rezultatai taip pat gali būti įrašyti į eilę, arba užduotys gali neduoti jokio rezultato (pavyzdžiui, jei užduotis skirta animacijai).
Paprastai užduočių yra daug daugiau nei gijų. Kai gija baigs savo užduotį, ji pareikalaus iš eilės kitos užduoties, kol visos užduotys bus baigtos. Tada srautas gali būti nutrauktas arba užmigti. Naudojamų gijų skaičius yra parametras, kurį galima suderinti, kad būtų užtikrintas geriausias našumas. Be to, siūlų skaičius gali būti dinamiškas, atsižvelgiant į atsirandančių užduočių skaičių. Pvz., Žiniatinklio serveris gali pridėti srautus, jei pateikiamos užklausos dėl kelių tinklalapių, ir gali ištrinti srautus, kai yra mažiau užklausų. Didėjant siūlų fondo dydžiui, didėja kompiuterio išteklių naudojimas. Algoritmas, naudojamas nustatyti, kada kurti ar sunaikinti gijas, turės įtakos bendram našumui: - Per daug gijų reiškia eikvoti išteklius ir laiką.
Sunaikinkite per daug gijų ir daugiau laiko praleisite vėliau, kad jas sukurtumėte - Sukūrus temas per lėtai, klientas gali būti prastesnis.
Kodėl reikalinga „ThreadPoolExecutor“ klasė?
viešosios klasės „ThreadPoolExecutor“ plėtoja „AbstractExecutorService“
„ExecutorService“, kuri vykdo kiekvieną pateiktą užduotį, naudodama vieną iš kelių galimų telkimo gijų, paprastai sukonfigūruotą naudojant vykdytojų gamyklinius metodus.
Gijų telkiniai išskiria dvi skirtingas problemas: paprastai jie pagerina našumą, atlikdami didelius asinchroninių užduočių kiekius, nes sumažėja skambučių pridėtinės išlaidos kiekvienai užduočiai, ir jie suteikia galimybę apriboti ir valdyti išteklius, įskaitant siūlus, naudojamus užduočių rinkiniui atlikti. Kiekvienas „ThreadPoolExecutor“ taip pat palaiko pagrindinę statistiką, pavyzdžiui, atliktų užduočių skaičių.
Kad ši klasė būtų naudinga įvairiose situacijose, joje yra daug reguliuojamų parametrų ir išplečiamųjų svirtelių. Tačiau programuotojai yra įsitikinę, kad naudoja patogesnius „Executors“ gamyklos metodus „Executors.newCachedThreadPool () (neribotas gijų telkinys, su automatiniu gijų atkūrimu), Executors.newFixedThreadPool (int) (fiksuoto dydžio sriegių fondas) ir Executors.newSingleThreadExecutor () (vieno fono gija). kurie iš anksto sukonfigūruoja dažniausiai naudojamų atvejų parametrus.
Ar žinote, kiek būdų sukurti giją?
Kalbos lygiu yra du būdai, kaip sukurti giją. „Java.lang.Thread“ klasės objektas yra gija, tačiau jai vykdyti reikia užduoties, kuri yra objektas, įgyvendinantis „java.lang.Runnable“ sąsają. Kadangi „Thread“ klasė įgyvendina „Runnable“ sąsają, galite nepaisyti „run ()“ metodo paveldėdami savo klasę iš „Thread“ arba įdiegdami joje „Runnable“ sąsają.
Kam naudojama „Ateities“ klasė?
Ateitis kaupia asinchroninio skaičiavimo rezultatą. Galite pradėti skaičiavimą pateikdami kam nors Ateities objektą ir jį pamiršę. Objekto Ateitis savininkas rezultatą gali gauti, kai jis bus paruoštas.
Kokie yra „Callable“ pranašumai prieš „Runnable“?
Callable sąsaja yra kur kas labiau tinkama užduotims, skirtoms lygiagrečiam vykdymui, kurti nei „Runnable“ sąsaja arba „Thread“ klasė. Verta paminėti, kad galimybė pridėti tokią sąsają atsirado tik nuo „Java 5“, nes svarbiausia „Callable“ sąsajos savybė yra parametrizuotų tipų (generinių), kaip parodyta sąraše, naudojimas.
Sąrašai Užduoties kūrimas naudojant „Callable 10 1“ importo „Java“ sąsają. util. vienu metu. Galima skambinti 11 2 viešosios klasės „CallableSample“ įgyvendina „Callable“ (12 3 viešų stygų kvietimas () meta išimtį (13 4, jei (kokia nors sąlyga)) (14 5 mesti naują „IOException“ ( "klaida apdorojant užduotį"); 15 6) 16 7 sistema. iš. println („užduotis apdorojama“); 17 8 grąžinti „rezultatą“; 18 9) 19 10)
Nedelsiant reikia atkreipti dėmesį į 2 eilutę, kur nurodoma, kad Callable sąsaja yra parametrizuota, o jos konkretus įgyvendinimas - CallableSample klasė priklauso nuo stygos tipo. 3 eilutėje rodomas pagrindinio skambučio metodo parašas jau parametruotoje versijoje, nes eilutės tipas taip pat nurodomas kaip grįžimo tipas. Iš tikrųjų tai reiškia, kad buvo sukurta užduotis, kurios rezultatas bus „String“ tipo objektas (žr. 8 eilutę). Tokiu pat būdu galite sukurti užduotį, kurios metu bet kokio tipo objektas bus sukurtas ir grąžintas naudojant skambučio metodą. Toks sprendimas yra daug patogesnis, palyginti su vykdymo metodu „Runnable“ sąsajoje, kuris nieko neduoda (jo grąžinimo tipas negalioja), todėl norint išgauti užduoties rezultatą reikia sugalvoti apeiti būdus.
Kitas „Callable“ sąsajos pranašumas yra galimybė „išmesti“ išimtis nepažeidžiant kitų vykdomų užduočių. 3 eilutė nurodo, kad išimties tipo išimtį galima „išmesti“ iš metodo, o tai iš tikrųjų reiškia bet kokią išimtį, nes visos išimtys yra java.lang.Exception palikuonys. 5 eilutėje ši funkcija naudojama kuriant patikrintą (patikrintą) IOException tipo išimtį. Paleidžiamos sąsajos vykdymo metodas visai neleido mesti kontroliuojamų išimčių, o nekontroliuojamos (vykdymo laiko) išimties metimas privertė sustoti giją ir visą programą.
Ar įmanoma atšaukti užduotį, jei naudojatės „Ateities“ klase?
Remiantis šia diskusija, iškelta ant stebulės, paaiškėja, kad neįmanoma.
Ateitis turi „Future.cancel“ (boolean) metodą, kuris turėtų atšaukti užduoties vykdymą. Bet jei užduotis jau prasidėjo, paskambinę „Future.cancel“ (tiesa) jos tikrai nesustabdysite. „FutureTask“ diegimo versijose kodas vykdomas:
if (mayInterruptIfRunning) (Sriegis r \u003d bėgikas; jei (r! \u003d nulis) r. pertraukimas ();)Tie. vėlgi, siūlai, kuriuose vykdoma užduotis, patariami tik sustabdyti vykdymą. Be to, mes net neturime galimybės išsiaiškinti, ar užduotis šiuo metu vykdoma, ar ne. Yra metodas „Future.isDone ()“, tačiau vėl jis grįžta tiesa ne tik tada, kai užduotis buvo baigta vykdyti, bet iškart po to, kai paskambinsite „Future.cancel“ (), net jei užduotis vis dar vykdoma (juk „Future.cancel“ (tiesa) nesustabdo jau pradėtos vykdyti užduoties).
Na, jei mes patys parašysime visą kodą, tada galėsite kruopščiai apdoroti „Thread.isInterrupted ()“ tinkamose vietose ir viskas bus gerai. Bet jei mes vykdysime trečiosios šalies kodą? Ar turėtume išplėsti serverį su papildiniais? Kai kurie kreivai parašyti papildiniai gali lengvai sukelti neveikiančią viso serverio būseną, nes mes negalime teisingai nutraukti įšaldyto papildinio vykdymo.
Rusų terminijoje už termino Siūlas Sustiprintas vertimas „Srautas“. Nors šį žodį taip pat galima išversti kaip „gija“. Kartais užsienio mokymo medžiagoje srauto sąvoka yra tiksliai paaiškinta gijomis. Tęsiame loginę seriją - ten, kur yra siūlai, yra ir rutulys. O kur kamuolys, ten katė. Iš karto akivaizdu, kad vertėjai neturėjo kačių. Taigi kilo painiava. Be to, pagal šį terminą yra ir kitų gijų Srautas. Vertėjai paprastai yra keistai atrodantys žmonės.
Paleidus bet kurią programą, vadinama gija pagrindinis srautas (pagrindinis). Iš jo generuojami vaikų srautai. Pagrindinė gija paprastai yra paskutinė gija, kuria siekiama nutraukti programą.
Nepaisant to, kad pagrindinis sriegis sukuriamas automatiškai, jį galima valdyti per klasės objektą Siūlas. Norėdami tai padaryti, paskambinkite metodu currentThread (), po kurio galite kontroliuoti srautą.
Klasė Siūlas yra keletas srautų valdymo metodų.
- getName () - gaukite srauto pavadinimą
- „getPriority“ () gauti prioritetą gijai
- gyvas () - nustatyti, ar siūlas teka
- prisijungti () - laukti upelio pabaigos
- bėgti () - pradėk srautą. Įrašykite joje savo kodą
- miegoti () - pristabdyti srautą tam tikrą laiką
- pradžia () - pradėti srautą
Gausime informaciją apie pagrindinę giją ir pakeisime jos pavadinimą.
Gija mainThread \u003d Thread.currentThread (); „mInfoTextView.setText“ („Dabartinė gija:“ + mainThread.getName ()); // Pakeiskite pavadinimą ir rodymą teksto lauke mainThread.setName („CatThread“); mInfoTextView.append ("\\ nNaujos gijos pavadinimas:" + mainThread.getName ());
Numatytasis pagrindinio gijos pavadinimas pagrindiniskurį mes pakeitėme Kateteris.
Gausime informacijos apie srauto pavadinimą, nenurodydami metodo.
Gija mainThread \u003d Thread.currentThread (); mInfoTextView.setText ("Dabartinė gija:" + mainThread);
Tokiu atveju galite pamatyti liniją Siūlas - srauto pavadinimas, jo prioritetas ir grupės pavadinimas.
Sukurkite savo srautą
Sukurti savo srautą nėra sunku. Pakanka paveldėti iš klasės Siūlas.
Mes deklaruojame vidinę klasę savo klasės viduje ir vadiname ją spustelėdami, vadindami metodą pradžia ().
Viešosios klasės „MyThread“ pratęsia temą (viešas negaliojimas vykdomas () (Log.d (TAG, „Mano gija veikia ...“);)) viešas negaliojimas onClick (rodinio vaizdas) (MyThread myThread \u003d new MyThread (); myThread.start ( );)
Arba galite perduoti metodo skambutį pradžia () statytojui.
„Public void onClick“ (Peržiūrėti rodinį) („MyThread myThread \u003d new MyThread ();) viešos klasės„ MyThread “pratęsia giją (// Constructor MyThread () (//) Sukurkite naują giją„ Super “(„ Antrasis gija “);„ Log.i “(TAG“) Sukuriamas antrasis srautas „+ tai“; start (); // Pradėti srautą) public void run () (Log.d (TAG, „Mano srautas veikia ...“); Pabandykite (už (int i \u003d 5; i\u003e 0; i--) (Log.i (TAG, „Antrasis siūlas:„ + i); Thread.sleep (500);)) pagauti („InterruptedException e“) (Log.i (TAG, „Antrasis siūlas nutrauktas“); )))
Vykdomos gijos sukūrimas
Yra sudėtingesnis srauto kūrimo variantas. Norėdami sukurti naują giją, turite įdiegti sąsają Paleidžiama. Srautą galite sukurti iš bet kurio objekto, įgyvendinančio sąsają. Paleidžiama ir paskelbti metodą bėgti ().
Vidinis metodas bėgti () jūs siunčiate naujos gijos kodą. Ši gija baigsis, kai metodas grįš.
Kai jūs paskelbiate naują klasę su sąsaja Paleidžiamareikia naudoti konstruktorių:
Gija (Vykdomas gijos_objektas, Styginių gijos_pavadinimas)
Pirmasis parametras nurodo klasės, įgyvendinančios sąsają, egzempliorių. Tai nustato, kur bus pradėtas gijos vykdymas. Antrasis parametras perduoda srauto pavadinimą.
Sukūrę naują giją, turite pradėti ją naudodami metodą pradžia (), kuris iš esmės sukelia metodo skambutį bėgti ().
Sukurkite naują giją mokymo projekte kaip įdėtą klasę ir paleiskite ją.
Paketas en.alexanderklimov.expresscourse; importuoti android.os.Bundle; importuoti „android.support.v7.app.AppCompatActivity“; importuoti android.util.Log; importuoti „android.view.View“; importuoti „android.widget.Button“; importuoti „android.widget.EditText“; importuoti „android.widget.TextView“; importuoti statinį ru.alexanderklimov.expresscourse.R.id.textViewInfo; viešosios klasės „MainActivity“ išplečia „AppCompatActivity“ (galutinė eilutės TAG \u003d „ExpressCourse“; privatus mygtukas „mButton“; privatus „EditText“ mResultEditText; privatus „TextView“ „mInfoTextView“; „@Override“ apsaugota negaliojanti „onCreate“ („Bundle savedInstanceState“) (super.onCreate. ); mButton \u003d (Mygtukas) findViewById (R.id.buttonGetResult); mResultEditText \u003d (EditText) findViewById (R.id.editText); mInfoTextView \u003d (TextView) findViewById (textViewidfo); „MyRunnable“); // sukurkite naują gijos bandymą (for (int i \u003d 5; i\u003e 0; i--)) („Log.i“ (TAG, „Pagrindinis gija:“ + i); „Thread.sleep“ (1000);) ) pagauti („InterruptedException e“) (Log.i (TAG, „Pagrindinis sriegis nutrauktas“);)) „MyRunnable“ padargai „Runnable“ (sriegio sriegis; // Constructor MyRunnable () (// Sukurti naują antrą sriegio sriegį \u003d naują giją (šį, "Sriegis, pavyzdžiui"); Log.i (TAG, "Antroji gija sukurta" + sriegis); thread.start (); // Vykdyti p otok) // Reikalingas „Runnable“ sąsajos viešojo tuštumos paleidimo () metodas (bandykite (už (int i \u003d 5; i\u003e 0; i--) („Log.i“ (TAG, „Antras siūlas:„ + i); „Thread.sleep" (500);)) pagauti („InterruptedException e“) („Log.i“ (TAG, „Antrasis siūlas nutrauktas“;))) )))
Viduje konstruktorius „MyRunnable“ () mes sukuriame naują klasės objektą Siūlas
Gija \u003d nauja gija (tai, pavyzdžiui, "Gija, pavyzdžiui");
Pirmasis parametras panaudojo objektą tai, o tai reiškia norą paskambinti metodu bėgti () šis objektas. Kitas metodas vadinamas. pradžia (), todėl pradedamas gijos vykdymas, pradedant nuo metodo bėgti (). Savo ruožtu metodas pradeda mūsų siūlų kilpą. Paskambinus metodui pradžia ()konstruktorius „MyRunnable“ () grąžina programos valdymą. Kai pagrindinis siūlas tęsia savo darbą, jis įeina į savo ciklą. Po to abu siūlai vykdomi lygiagrečiai.
Galite paleisti kelis, o ne tik antrą, ne tik pirmąjį, bet ir kitus. Tai gali sukelti problemų, kai dvi gijos bando dirbti tuo pačiu kintamuoju tuo pačiu metu.
Sinchronizuotas raktinis žodis - sinchronizuoti metodai
Norėdami išspręsti problemą su gijomis, kurios gali kelti painiavą, naudojama sinchronizacija.
Metodas gali turėti modifikatorių. sinchronizuotas. Kai gija yra sinchronizuoto metodo viduje, visi kiti gijos, bandantys skambinti tuo pačiu atveju, turėtų palaukti. Tai pašalina painiavą, kai kelios gijos bando panaudoti metodą.
Sinchronizuotas void meow (eilutės žinutė);
Be to, raktinis žodis sinchronizuotas gali būti naudojamas kaip operatorius. Galite uždaryti bloke sinchronizuotas metodo skambučiai kai kuriose klasėse:
Sinchronizuotas (objektas) (// sakiniai, kuriuos reikia sinchronizuoti)
Looperis
Srautas turi subjektus Looperis, Prižiūrėtojas, „MessageQueue“.
Kiekvienas siūlas turi vieną unikalų Looperis ir gali turėti daug Prižiūrėtojas.
Grafas Looperis pagalbinis srauto objektas, kuris jį valdo. Jis apdoroja gaunamus pranešimus, taip pat nurodo gijai nutraukti reikiamu metu.
Srautas gauna savo Looperis ir „MessageQueue“ per metodą „Looper.prepare“ () po paleidimo. „Looper.prepare“ () identifikuoja skambinančią giją, sukuria Looperis ir „MessageQueue“ ir suriša srautą su jais saugykloje Threadlocal. Metodas „Looper.loop“ () turėtų būti pašauktas bėgti Looperis. Jos darbą galite atlikti atlikdami metodą looper.quit ().
„Class LooperThread“ pratęsia giją („Public Handler mHandler“; „public void run“) („Looper.prepare (); mHandler \u003d new Handler“) („public void handleMessage“ („Message msg“) (// čia apdorokite gaunamus pranešimus); „Looper.loop“) ;))
Naudokite statinį metodą getMainLooper () prieiti Looperis pagrindinis siūlas:
„Looper mainLooper“ \u003d „Looper.getMainLooper“ ();
Sukurkite du siūlus. Mes pradėsime vieną pagrindiniame sraute, o antrą atskirai nuo pagrindinio. Mums užteks dviejų mygtukų ir etiketės.
Atkreipkite dėmesį, kaip prasideda siūlai. Pirmasis siūlas pradedamas naudojant metodą pradžia (), o antra - bėgti (). Tada mes patikriname, kuriame sraute mes esame.
Paketas ru.alexanderklimov.as23; importuoti android.os.Bundle; importuoti android.os.Looper; importuoti „android.support.v7.app.AppCompatActivity“; importuoti „android.view.View“; importuoti „android.widget.Button“; importuoti „android.widget.TextView“; viešosios klasės „MainActivity“ išplečia „AppCompatActivity“ („TextView mInfoTextView“; „@Orreide“ apsaugota negaliojanti „onCreate“ („Bundle savedInstanceState“) (super.onCreate (savedInstanceState); setContentView (R.layout.activity_main); Button startButton_ (Button) Mygtukas runButton \u003d (mygtukas) findViewById (R.id.button_run); mInfoTextView \u003d (TextView) findViewById (R.id.textview_info); startButton.setOnClickListener (naujas vaizdas.OnClickListener () („@Override“ viešas tuštuma) Gijos sriegis \u003d nauja gija (nauja „MyRunnable ()“; thread.start (); // foniniame sriegyje))); runButton.setOnClickListener (naujas „View.OnClickListener“) () (@Override public void onClick (View view)) (Thread thread) \u003d nauja gija (nauja MyRunnable ()); thread.run (); // dabartinėje gijoje)));) privačios klasės „MyRunnable“ padargai Runnable (@Orride public void run () (// Patikrinkite, kurioje gijoje esame, jei (( „Looper.getMainLooper“). „GetThread“) \u003d\u003d „Thread.currentThread ()“ („mInfoText“) „View.setText“ („Pagrindiniame sraute“); ) else (runOnUiThread (new Runnable () (@Override public void run () (mInfoTextView.setText („Fono gijoje“;))));)))))
Ši tema yra gana sudėtinga ir daugumai jos nedomina ir reikia studijuoti.
„Android“ sistemoje grynieji srautai naudojami vis mažiau, sistema turi savo būdų.
Į kokias būsenas gali patekti gija įvesdama sinchronizuotą bloką?
- VEIKIAMOS
- BLOKUOTAS
„RUNNABLE“, jei kodų blokas, pažymėtas sinchronizuotu, nėra užimtas kitu gija. Priešingu atveju mūsų gija gaus BLOCKED būseną ir lauks, kol mutex objektas bus paleistas.
Jei paskambinsite šiuo metodu, gija bus laukiama.
Laukimo () metodą galima vadinti tik sintetiniame „mutex“ objekto, kurį „užrakino (užrakino)“ dabartinė gija, viduje, kitaip metodas įmes išimtį IllegalMonitorStateException.
Objekto stebėtojas \u003d getMonitor (); sinchronizuotas (monitorius) (... monitorius. laukti (); ...)
Kai iškviečiamas laukimo () metodas, dabartinis siūlas atrakina monitoriaus objektą ir pereina į WAITING būseną, laukdamas, kol monitor.notify () arba monitor.notifyAll () metodas bus iškviestas kitu siūlu. Kai tik tai atsitiks, siūlas pabus ir jei monitorius nebuvo užimtas, tada jis jį užfiksuos ir toliau veiks.
Į kokią būseną pateks gija, kai bus vadinamas laukimo (500) metodas?
Paskambinus šiuo metodu, sriegis tampa TIMED_WAITING būsena.
Pagal analogiją su laukimo () metodu laukimas (pertrauka) gali būti vadinamas tik mutex objekto sinchronizuotame bloke, kurį „užrakino (užrakino)“ dabartinė gija. Objekto stebėtojas \u003d getMonitor (); sinchronizuotas (monitorius) (... monitorius. laukti (500); ...)
Kai vadinamas laukimo () metodas, dabartinis siūlas atrakina monitoriaus objektą ir užmiega 500 milisekundžių. Monitoriaus objektą gali užfiksuoti kita gija.
Po 500 milisekundžių siūlas atsibunda ir jei monitorius nebuvo užimtas, tada jis jį užfiksuos ir toliau dirbs.
Jei monitorius užimtas kitu sriegiu, dabartinis sriegis pereis į BLOCKED būseną.
Į kokią būseną pateks gija, kai bus vadinamas pranešimo () metodas?
Objekto stebėtojas \u003d getMonitor (); sinchronizuotas (monitorius) (... monitorius. laukti (); ...)
Po „monitor.wait“ () gijos pereina į WAITING būseną. Pranešimo () metodas, vadinamas kitu monitoriaus objekto siūlu, perkels siūlą iš WAITING būsenos į RUNNABLE būseną, jei monitoriaus objektas nėra užfiksuotas kitu siūlu, kitaip - BLOCKED būsena.
Į kokią būseną pateks gija, kai bus pašauktas metodas „AlAll () “?
„NotifyAll“ () „pažadina“ visas gijas. Viena iš WAITING gijų pateks į RUNNABLE būseną, užfiksuos panaudoto objekto monitorių ir tęs savo darbą. Likusi dalis bus užblokuota. Kai tik pirmasis „pažadintas“ siūlas paleidžia monitorių, kurio visi kiti laukia, kitas siūlas pakartos savo likimą (savavališkas siūlas iš BLOKUOTOS būsenos pereis į RUNNABLE būseną). Tai tęsis tol, kol visi pažadinti siūlai paliks BLOCKED būseną.
Trys sinchronizuoto bloko sriegiai, vadinami „mutex“ objekto laukimu (). Į kokią būseną pateks šios gijos, jei ketvirtasis gijų skambutis pranešaAll ()?
Du iš jų pereis į užblokuotą būseną, vienas į RUNNABLE būseną
Kuo skiriasi prisijungimas (500) ir laukimas (500)?
Nepaisant to, kad abu prisijungti (500) ir laukti (500) dabartinę giją perkelia į TIMED_WAITING būseną, tarp jų yra didelių skirtumų:
jungtis (500) vadinama sriegiu, laukti (500) yra vadinama objekto, per kurį sinchronizuojamas duotas blokas, sinchronizuoto bloko viduje.
Kai iškviečiamas prisijungimas (500), dabartinis siūlas palauks 500 milisekundžių, kad baigtų sriegį, kurio sujungimo () metodas buvo vadinamas.
Po 500 milisekundžių abiem atvejais siūlai ir toliau dirba.
Kuo skiriasi laukimas (500) ir miegas (500)?
Miegas (500) vadinamas gija, laukimas (500) vadinamas objekto, per kurį sinchronizuojamas šis blokas, sinchronizuotame bloke.
Paskambinus miegui (500), dabartinė gija lauks 500 milisekundžių, tada tęs savo darbą.
Kai iškviečiamas laukimas (500), dabartinis siūlas atrakina sinchronizuotą objektą ir užmiega 500 milisekundžių.
Į kokią būseną pateks siūlai, kai vadinamas išeigos () metodas?
Kai vadinamas išeigos () metodas, dabartinis siūlas „praleidžia savo apsisukimą“ ir „Java“ iškart pereina į kitą siūlą. Siūlas iš veikiančios būsenos pereina į parengties būseną. Bėgimo būsenos ir parengties būsenos yra RUNNABLE būsenos pogrupiai.
Siūlo klasė
„Thread“ klasė yra pati elementariausia iš visų „System.The vardų erdvės tipų“. Ši klasė nurodo į objektą orientuotą apvyniojimą aplink nurodytą vykdymo kelią konkretaus „AppDomain“ viduje. Šis tipas taip pat apibūdina metodų rinkinį (tiek statinį, tiek egzemplioriaus lygį), leidžiančius sukurti naujus gijas dabartiniame „AppDomain“, taip pat pristabdyti, sustabdyti ir sunaikinti tam tikrą giją. Pagrindinių statinių narių sąrašas pateiktas žemiau:
Dabartinis kontekstasŠi tik skaitoma nuosavybė grąžina kontekstą, kuriame šiuo metu veikia gija.
Dabartinė gijaŠi tik skaitoma nuosavybė pateikia nuorodą į šiuo metu vykdomą giją.
„GetDomain“ (), „GetDomainID“ () Miegoti ()Šis metodas pristabdo esamą giją nurodytam laikui.
„Thread“ klasė taip pat palaiko kelis egzemplioriaus lygio metodus, kai kurie iš jų aprašyti žemiau esančioje lentelėje. Aktyvaus gijos atšaukimas ar pristabdymas paprastai laikomas bloga idėja. Kai tai padarysite, yra tikimybė (nors ir nedidelė), kad siūlas gali nutekėti iš savo darbo krūvio, kai jis sutrinka ar nutrūksta.
Institucijos lygio narys | Paskyrimas |
Gyvas | Gauna loginę reikšmę, nurodančią, ar gija veikia (o dar nenutraukta ar atšaukta) |
Isbackground | Gauna arba nustato vertę, nurodančią, ar šis srautas yra „fonas“ (išsamiau paaiškinta žemiau) |
vardas | Leidžia nustatyti draugiško teksto srauto pavadinimą |
Prioritetas | Gauna arba nustato gijos prioritetą, kuris gali paimti vertę iš „ThreadPriority“ sąrašo. |
Sriegio būsena | Gauna šios gijos būseną, kuriai galima priskirti vertę iš „ThreadState“ sąrašo. |
Nutraukti () | Paveda CLR kuo greičiau nutraukti siūlą |
Pertraukti () | Nutraukia (t. Y. Sustabdo) esamą siūlą nurodytam laukimo laikotarpiui |
Prisijunkite () | Užblokuoja skambinančią giją, kol nenutrūks nurodytas gija (ta, kuria vadinamas Join ()) |
Aprašymas () | Atkuria anksčiau sustabdytą srautą |
Pradėti () | Paveda CLR kuo greičiau pradėti giją |
Sustabdyti () | Pristabdo siūlą. Jei gija jau pakabinta, skambinimas Suspend () neturi jokios įtakos |
Statistikos apie esamą giją gavimas
Prisiminkite, kad vykdomojo rinkinio (t. Y. Metodo Main ()) pradinis taškas eina pirminėje vykdymo gijoje. Tarkime, kad turite naują konsolės programą, norėdami parodyti pagrindinį „Thread“ tipo naudojimą. Kaip žinote, statinė nuosavybė „Thread.CurrentThread“ nuskaito „Thread“ objektą, kuris žymi šiuo metu vykdomą giją. Gavę dabartinį srautą, galite pateikti įvairius statistinius duomenis apie jį:
Naudojant sistemą; naudojant „System.Collections.Generic“; naudojant „System.Linq“; naudojant „System.Text“; naudojant „System.Threading“; vardų sritis ConsoleApplication1 (klasės programa (statinė negaliojanti Main () (Console.Title \u003d "(! LANG: Informacija apie pagrindinės programos srautą)"; 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(); } } }!}
Nors šis kodas yra daugiau ar mažiau akivaizdus, \u200b\u200batkreipkite dėmesį, kad gijų klasė palaiko savybę pavadinimu. Jei tiksliai nenustatysite jo vertės, tada „Name“ grįš tuščia eilutė. Drauginio vardo priskyrimas konkrečiam „Thread“ objektui gali žymiai palengvinti derinimo procesą. Derinimo seanso metu „Visual Studio 2010“ galite atidaryti temų langą, pasirinkdami Debug -\u003e Windows -\u003e Threads iš meniu (Derinimas -\u003e Windows -\u003e Threads).
Atminkite, kad nuosavybė pavadinimu Prioritetas yra apibrėžta sriegio tipu. Pagal numatytuosius nustatymus visų gijų prioriteto lygis yra normalus. Tačiau tai galima pakeisti bet kuriuo sriegio gyvavimo ciklo momentu, naudojant nuosavybę „Thread.Priority“ ir su ja susijusią sistemą.Threads.ThreadPriority uzskaitymas:
„Public enum“ „ThreadPriority“ (žemiausia, žemesnė nei normali, normali, // numatytoji vertė. Aukščiau esančio, aukščiausia)
Nustatant gijos prioriteto lygį į kitą nei numatytąją vertę („ThreadPriority.Normal“), reikia atsiminti, kad tai tiesiogiai nekontroliuoja, kaip gijų planavimo priemonė perjungs sriegius tarpusavyje. Tiesą sakant, gijos prioriteto lygis suteikia CLR užuominą apie gijos veiksmų svarbą. Taigi ne visada garantuojama, kad gija su „ThreadPriority.Highest“ prioriteto lygiu gaus aukščiausią prioritetą.
Tarkime, kad jūs rašote dujotiekį, kuriame 2 gijos apdoroja duomenis naudodami bendrą buferį. Gamintojų srautas sukuria šiuos duomenis, o vartotojų srautas juos apdoroja (gamintojo - vartotojo problema). Šis kodas yra paprasčiausias modelis: naudodamiesi std :: gija, mes sukuriame vartotojų srautą ir sukuriame duomenis pagrindiniame sraute.
Void product () (// sukurkite užduotį ir įdėkite ją į eilę) void consume () (// perskaitykite duomenis iš eilės ir apdorokite) int main (int, char **) (std :: thread thr (sunaudoti); // generuoti srautas gaminti (); // kurti duomenis apdoroti thr.join (); // laukti, kol funkcija veiks, sunaudos () grįš 0;)
Praleidome dviejų gijų sinchronizacijos mechanizmus ir atkreipiame dėmesį į pagrindinę () funkciją. Pabandykite atspėti, kas negerai su šiuo kodu, ir kaip jį ištaisyti?
Tarkime, kad suvartoti () funkcija yra išimtis. Kadangi ši išimtis yra įmesta į vaiko siūlą, jūs negalite jo sugauti ir valdyti pagrindiniame sriegyje. Jei plečiant vaikiškų gijų pluoštą nebuvo tinkamo išimčių tvarkytojo, bus iškviesta funkcija std :: terminate (), kuri pagal nutylėjimą vadinsis aborto () funkcija. Kitaip tariant, jei netvarkysite išimties sriegyje, kurį sukuria objektas thr, programa išeis su klaida.
Naudoti funkciją product () yra šiek tiek sudėtingiau. Tarkime, kad ši funkcija yra išimtis. Pirmas dalykas, kurį norite padaryti, yra apvynioti main () pagrindą bandymo sugauti bloke:
Pabandykite (std: sriegis thr (suvartoti); gaminti (); // meta išimtį thr.join ();) pagauti (...) ()
Atrodo, kad problema buvo išspręsta, bet jei bandysite paleisti šį kodą, programa vis tiek sugenda. Kodėl tai vyksta? Leiskime tai teisingai.
std :: gija
Kaip jau spėjote atspėti, problema yra susijusi ne su dujotiekiu, o su teisingu standartinių bibliotekos vykdymo gijų naudojimu. Visų pirma, ši apibendrinta funkcija yra lygiavertė ir turi tas pačias problemas:
Tuščias važiavimas (funkcija
Prieš pradėdami spręsti savo problemą, trumpai prisiminkime, kaip veikia „std ::“ siūlai.
1) konstruktorius paleidimui:
Šablonas
Inicijavus std :: gijos objektą, sukuriamas naujas gija, kurioje paleidžiama fn funkcija su galimais args argumentais. Sėkmingai sukūrus, konkretus objekto egzempliorius pradeda reprezentuoti šį srautą pirminiame sraute, o objekto ypatybėse nustatoma sujungimo vėliava.
Atminkite: sujungiamas objektas yra susijęs su srautu.
2) Laukiame, kol baigsis sukurto gijos vykdymas:
Tuščias siūlas :: join ();
Šis metodas blokuoja tolesnį pirminės gijos vykdymą, kol vaikas baigs. Po sėkmingo vykdymo srauto objektas nustoja jį reprezentuoti, nes mūsų srauto nebeegzistuoja. Prisijungimo vėliava yra atmesta.
3) Nedelsdami „atjunkite“ objektą nuo srauto:
Tuščias siūlas :: atsegti ();
Tai neblokuojantis metodas. Prisijungimo vėliava yra atstatoma, o vaiko siūlai paliekami savo prietaisams ir savo darbą baigs vėliau.
4) naikintojas:
Siūlas :: ~ siūlas ();
Naikintojas sunaikina objektą. Be to, jei šis objektas turi jungiamąją vėliavą, tada iškviečiama funkcija std :: terminate (), kuri pagal nutylėjimą vadina aborto () funkciją.
Dėmesio! Jei sukūrėme objektą ir siūlą, bet nekvietėme prisijungti ar atsiriboti, tada programa suduš. Iš principo tai yra logiška - jei objektas vis dar yra prijungtas prie srauto, tada jūs turite su juo ką nors padaryti. Dar geriau, nieko nedaryti ir baigti programą (bent jau taip nusprendė standartinis komitetas).
Todėl, kai gaminio () funkcijai įvyksta išimtis, mes stengiamės sunaikinti jungiamąjį objektą.
Apribojimai
Kodėl standartinis komitetas nusprendė tai padaryti, o ne kitaip? Ar ne geriau būtų vadinti prisijungti () arba nuimti () į naikintoją? Pasirodo, ne ką geriau. Pažvelkime į abu šiuos atvejus.
Tarkime, kad turime „joining_thread“ klasę, kuri savo destruktoriuje kviečia prisijungti ():
Sujungimo_tisas :: ~ sujungimo_tasis () (prisijungti ();)
Tada, prieš pradėdami tvarkyti išimtį, turėsime palaukti, kol vaiko siūlai baigs veikti, nes join () blokuoja tolesnį programos vykdymą. Ir jei taip atsitiko, kad sukurtas srautas pateko į begalinę kilpą?
Panaikinti sunaudojimą () (o (1) (...)) ... pabandyti (prisijungti_sriegis (sunaudoti); mesti std :: išimtis ();) sugauti (...) (// gali netrukus įvykti, arba niekada)
Na, mes sužinojome, kad geriau nekviesti destruktoriaus join () (kol nebūsite tikri, kad tai teisingas įvykių tvarkymas), nes tai yra blokavimo operacija. O kaip atskirti ()? Kodėl nepavadinus šio blokuojančio metodo naikintoju, leidžiantį pagrindiniam siūlui tęsti darbą? Tarkime, kad mes turime tokią klasę atskyrimo_sriegis.
Bet tada galime patekti į situaciją, kai sugeneruota gija bando naudoti išteklius, kurių nebėra, kaip ir šioje situacijoje:
Pabandykite (int duomenys; atskyrimas_sriegio gijos (sunaudoti, & duomenys); // šiuo atveju vartojimas nurodo rodyklę į int kaip argumentas mesti std :: poikkemas ()) pagauti (...) (// teisingai tvarkyti išimtį // vartoti toliau vykdomas, bet nurodo jau ištrintą duomenų objektą)
Taigi standarto kūrėjai nusprendė perkelti atsakomybę už programuotoją - galų gale jis geriau žino, kaip programa turėtų elgtis tokiais atvejais. Remiantis visa tai paaiškėja, kad standartinė biblioteka prieštarauja principui - kurdami „std ::“ siūlą, mes patys turime pasirūpinti tinkamu išteklių valdymu, tai yra, aiškiai vadinti prisijungimu ar atsiribojimu. Dėl šios priežasties kai kurie programuotojai pataria nenaudoti „std ::“ siūlų objektų. Kaip ir naujas, ir ištrinamas, „std ::“ siūlas suteikia galimybę jų pagrindu kurti aukštesnio lygio įrankius.
Sprendimas
Vienas iš tokių įrankių yra „Boost boost :: thread_joiner“ bibliotekos klasė. Aukščiau pateiktame pavyzdyje jis atitinka mūsų „joining_thread“. Jei galite sau leisti naudoti trečiųjų šalių bibliotekas darbui su srautais, tada geriau tai padaryti.