Suuri osa Java-säikeillä ohjelmoinnista opittavasta ei ole muuttunut dramaattisesti Java-alustan kehityksen aikana, mutta se on muuttunut vähitellen. Tässä Java-säikeiden alukkeessa Cameron Laird osuu joihinkin langan korkeisiin (ja mataliin) pisteisiin samanaikaisena ohjelmointitekniikkana. Hanki yleiskatsaus monisäikeisen ohjelmoinnin monivuotisesta haastavuudesta ja selvitä, kuinka Java-alusta on kehittynyt vastaamaan joihinkin haasteisiin.
Samanaikaisuus on Java-ohjelmoinnin uusien tulokkaiden suurimpia huolenaiheita, mutta ei ole mitään syytä antaa sen pelotella sinua. Ei vain erinomaista dokumentaatiota (tutkimme useita lähteitä tässä artikkelissa), mutta Java-ketjujen on ollut helpompi työskennellä Java-alustan kehittyessä. Jotta oppisit tekemään monisäikeisen ohjelmoinnin Java 6: ssa ja 7: ssä, tarvitset vain joitain rakennuspalikoita. Aloitamme näistä:
- Yksinkertainen kierteitetty ohjelma
- Threading on kyse nopeudesta, eikö?
- Java-samanaikaisuuden haasteet
- Milloin käyttää Runnable-ohjelmaa
- Kun hyvät säikeet menevät pieleen
- Mitä uutta Java 6: ssa ja 7: ssä
- Mitä seuraavaksi Java-säikeille
Tämä artikkeli on aloittelijan kysely Java-ketjutekniikoista, mukaan lukien linkit joihinkin JavaWorldin useimmin lukemiin monisäikeisiin ohjelmointiin liittyviin johdanto-artikkeleihin. Käynnistä moottorit ja seuraa yllä olevia linkkejä, jos olet valmis aloittamaan Java-ketjutuksen oppimisen jo tänään.
Yksinkertainen kierteitetty ohjelma
Harkitse seuraavaa Java-lähdettä.
Listaus 1. FirstThreadingExample
class FirstThreadingExample {public static void main (String [] args) {// Toinen argumentti on viive // peräkkäisten lähtöjen välillä. Viive // mitataan millisekunteina. "10" tarkoittaa esimerkiksi "tulosta viiva // sekunnin joka sadasosassa". ExampleThread mt = uusi ExampleThread ("A", 31); ExampleThread mt2 = uusi ExampleThread ("B", 25); ExampleThread mt3 = uusi ExampleThread ("C", 10); mt.start (); mt2.start (); mt3.start (); }} luokka ExampleThread laajentaa ketjua {private int delay; public ExampleThread (String label, int d) {// Anna tälle säikeelle // nimi: "thread 'LABEL'". super ("säie" "+ tarra +" ""); viive = d; } public void run () {for (int count = 1, row = 1; rivi <20; rivi ++, count ++) {kokeile {System.out.format ("Rivi #% d kohteesta% s \ n", count, getName ()); Thread.currentThread (). Lepotila (viive); } catch (InterruptedException ie) {// Tämä olisi yllätys. }}}}
Käännä ja suorita tämä lähde nyt kuten mikä tahansa muu Java-komentorivisovellus. Näet tuotoksen, joka näyttää tältä:
Listaus 2. Kierteellisen ohjelman tulos
Rivi # 1 ketjusta 'A' Rivi # 1 säikeestä C 4 langasta 'C' ... Linja # 17 langasta B # 16 langasta 'A' Viiva # 17 langasta 'A' Viiva # 18 langasta 'A' Linja # 19 langasta 'A'
Siinä se - olet Java Lanka
ohjelmoija!
No, okei, ehkä ei niin nopeasti. Niin pieni kuin luettelossa 1 oleva ohjelma on, se sisältää joitain hienovaraisuuksia, jotka ansaitsevat huomiomme.
Langat ja määrittelemättömyys
Tyypillinen oppimissykli ohjelmoinnilla koostuu neljästä vaiheesta: (1) Tutki uutta konseptia; (2) suorittaa näyteohjelma; (3) vertaa tuotosta odotuksiin; ja (4) toistetaan kahden ottelun ajan. Huomaa kuitenkin, että sanoin aiemmin tuotoksen FirstThreadingExample
näyttää "jotain" listalta 2. Joten, se tarkoittaa, että tuotoksesi voi olla erilainen kuin minun, rivi riviltä. Mitä? että noin?
Yksinkertaisimmissa Java-ohjelmissa on tae toteutuksen järjestyksestä: ensimmäinen rivi sisään main ()
suoritetaan ensin, sitten seuraava ja niin edelleen asianmukaisella jäljityksellä muihin menetelmiin. Lanka
heikentää tätä takuuta.
Langoittaminen tuo uutta tehoa Java-ohjelmointiin; voit saavuttaa tuloksia säikeillä, joita et voi tehdä ilman niitä. Mutta tuo voima tulee hinnalla päättäväisyyttä. Yksinkertaisimmissa Java-ohjelmissa on tae toteutuksen järjestyksestä: ensimmäinen rivi sisään main ()
suoritetaan ensin, sitten seuraava ja niin edelleen asianmukaisella jäljityksellä muihin menetelmiin. Lanka
heikentää tätä takuuta. Monisäikeisessä ohjelmassa "Viiva # 17 langasta B
"saattaa näkyä näytöllä ennen tai jälkeen"Rivi # 14 langasta A
, "ja järjestys saattaa vaihdella saman ohjelman peräkkäisissä suorituksissa, jopa samassa tietokoneessa.
Määrittelemättömyys voi olla tuntematon, mutta sen ei tarvitse olla häiritsevää. Toteutusjärjestys sisällä säie pysyy ennustettavissa, ja määrittelemättömyyteen liittyy myös etuja. Olet ehkä kokenut jotain vastaavaa työskennellessäsi graafisten käyttöliittymien kanssa. Tapahtumakuuntelijat Swingissä tai tapahtumankäsittelijät HTML-muodossa ovat esimerkkejä.
Vaikka ketjujen synkronoinnin täydellinen keskustelu on tämän johdannon ulkopuolella, perusasiat on helppo selittää.
Harkitse esimerkiksi HTML-määritelmän mekaniikkaa ... onclick = "myFunction ();" ...
määrittää toiminnan, joka tapahtuu käyttäjän napsautuksen jälkeen. Tämä tuttu epämääräisyystapa havainnollistaa joitain sen etuja. Tässä tapauksessa, myFunction ()
ei suoriteta tiettyyn aikaan lähdekoodin muihin osiin nähden, mutta suhteessa loppukäyttäjän toimintaan. Joten määrittelemättömyys ei ole vain järjestelmän heikkous; se on myös rikastuminen suoritusmallin, joka antaa ohjelmoijalle uusia mahdollisuuksia määrittää järjestys ja riippuvuus.
Suoritusviiveet ja langan alaluokka
Voit oppia FirstThreadingExample
kokeilemalla sitä itse. Yritä lisätä tai poistaa Esimerkki lanka
s - toisin sanoen konstruktoreiden kutsut ... uusi ExampleThread (tunniste, viive);
- ja nokkela viive
s. Perusajatuksena on, että ohjelma käynnistyy kolme erillistä Lanka
s, jotka sitten kulkevat itsenäisesti loppuun asti. Jotta niiden toteutus olisi opettavampaa, kukin viivästyy hieman tulostettavien peräkkäisten rivien välillä; tämä antaa muille ketjuille mahdollisuuden kirjoittaa heidän ulostulo.
Ota huomioon, että Lanka
-pohjainen ohjelmointi ei yleensä edellytä Keskeytetty poikkeus
. Kuvassa näkyvä FirstThreadingExample
liittyy nukkua()
sen sijaan, että se olisi suoraan yhteydessä Lanka
. Suurin osa Lanka
-pohjainen lähde ei sisällä a nukkua()
; tarkoitukseen nukkua()
Tässä on mallinnettava yksinkertaisella tavalla "luonnossa" löydettyjen pitkäkestoisten menetelmien käyttäytyminen.
Jotain muuta ilmoitettavaa luettelossa 1 on se Lanka
on abstrakti luokka, joka on suunniteltu luokiteltavaksi. Sen oletusarvo juosta()
method ei tee mitään, joten se täytyy ohittaa alaluokan määrittelyssä, jotta saavutetaan mitään hyödyllistä.
Tässä on kyse nopeudesta, eikö?
Joten nyt voit nähdä hieman siitä, mikä tekee ohjelmoinnista langoilla monimutkaisen. Mutta tärkein kohta kestää kaikki nämä vaikeudet ei ole saada nopeutta.
Monisäikeiset ohjelmat Älä, yleensä loppuun nopeammin kuin yksisäikeiset - itse asiassa ne voivat olla huomattavasti hitaampia patologisissa tapauksissa. Monisäikeisten ohjelmien perustavanlaatuinen lisäarvo on reagointikykyä. Kun JVM: n käytettävissä on useita prosessointisydämiä tai kun ohjelma viettää paljon aikaa odottaessaan useita ulkoisia resursseja, kuten verkkovastauksia, monisäikeisyys voi auttaa ohjelmaa suorittamaan nopeammin.
Ajattele graafista käyttöliittymäsovellusta: jos se silti reagoi loppukäyttäjän pisteisiin ja napsautuksiin etsiessään "taustalla" vastaavaa sormenjälkeä tai laskea uudelleen seuraavan vuoden tennisturnauksen kalenteria, se rakennettiin samanaikaisuutta ajatellen. Tyypillinen samanaikainen sovellusarkkitehtuuri asettaa tunnistamisen ja vastauksen käyttäjän toimiin erillisessä säikeessä, joka on erillinen laskennallisesta langasta, joka on osoitettu käsittelemään suurta taustakuormaa. (Katso "Peruuta langoitus ja tapahtuman lähetyslanka", jos haluat lisätietoja näistä periaatteista.)
Harkitse sitten todennäköisimmin omassa ohjelmoinnissasi Lanka
s yhdessä näistä olosuhteista:
- Olemassa olevalla sovelluksella on oikeat toiminnot, mutta se ei toisinaan vastaa. Nämä "lohkot" liittyvät usein ulkoisiin resursseihin, jotka eivät ole sinun hallinnassasi: aikaa vieviä tietokantakyselyjä, monimutkaisia laskelmia, multimediatoistoa tai verkkovastauksia hallitsemattomalla viiveellä.
- Laskennallisesti intensiivinen sovellus voisi hyödyntää paremmin monisydämisiä isäntiä. Näin voi olla joku, joka tekee monimutkaista grafiikkaa tai simuloi mukana olevaa tieteellistä mallia.
Lanka
ilmaisee luonnollisesti sovelluksen vaaditun ohjelmointimallin. Oletetaan esimerkiksi, että mallinnit ruuhka-auton kuljettajien tai mehiläisten käyttäytymistä pesässä. Kunkin kuljettajan tai mehiläisen toteuttaminen aLanka
liittyvä esine voi olla kätevä ohjelmoinnin kannalta, lukuun ottamatta nopeuden tai reagoivuuden näkökohtia.
Java-samanaikaisuuden haasteet
Kokenut ohjelmoija Ned Batchelder hiljattain
Jotkut ihmiset kohtaavat ongelman, ajattelevat: "Tiedän, käytän säikeitä", ja sitten heillä on kaksi erpoblesmia.Se on hauskaa, koska se mallintaa ongelmaa yhtäaikaisesti. Kuten jo mainitsin, monisäikeiset ohjelmat antavat todennäköisesti erilaisia tuloksia ketjun suorittamisen tarkan järjestyksen tai ajoituksen suhteen. Se on huolestuttavaa ohjelmoijille, jotka on koulutettu ajattelemaan toistettavien tulosten, tiukan päättäväisyyden ja muuttumattoman järjestyksen suhteen.
Se pahenee. Erilaiset säikeet eivät välttämättä tuota tuloksia eri järjestyksissä, mutta voivat väittävät tärkeämmillä tasoilla tulosten saavuttamiseksi. Uusille tulokkaille on helppo monisäikeinen kiinni()
tiedostokahva yhdessä Lanka
ennen erilaista Lanka
on saanut kaiken valmiiksi kirjoittamiseen.
Samanaikaisten ohjelmien testaaminen
Kymmenen vuotta sitten JavaWorldissa Dave Dyer totesi, että Java-kielellä oli yksi ominaisuus, jota "käytettiin niin paljon väärin", että hän luokitteli sen vakavaksi suunnitteluvirheeksi. Tämä ominaisuus oli monisäikeinen.
Dyerin kommentti korostaa monisäikeisten ohjelmien testaamisen haastetta. Kun et voi enää helposti määrittää ohjelman ulostuloa määrätyn merkkijonon mukaan, vaikuttaa siihen, kuinka tehokkaasti voit testata ketjutettua koodia.
Oikean lähtökohdan samanaikaisen ohjelmoinnin luontaisten vaikeuksien ratkaisemiseen totesi Heinz Kabutz hyvin Java Specialist -uutiskirjeessään: tunnustaa, että samanaikaisuus on aihe, jonka sinun tulisi ymmärtää, ja tutkia sitä systemaattisesti. On tietysti työkaluja, kuten kaaviotekniikat ja viralliset kielet, jotka auttavat. Mutta ensimmäinen askel on terävöittää intuitiotasi harjoittelemalla yksinkertaisilla ohjelmilla, kuten FirstThreadingExample
Luettelossa 1. Seuraavaksi opi niin paljon kuin voit seuraavien perusteiden langoittamisesta:
- Synkronointi ja muuttumattomat objektit
- Viestien ajoitus ja odotus / ilmoitus
- Kilpailuolosuhteet ja umpikuja
- Lankavalvojat tarjoavat yksinoikeuden, ehdot ja väitteet
- JUnitin parhaat käytännöt - monisäikeisen koodin testaaminen
Milloin käyttää Runnable-ohjelmaa
Java-objektin suuntaus määrittelee yksin perityt luokat, mikä vaikuttaa monisäikeisiin koodauksiin. Tähän kohtaan olen kuvannut vain käytön Lanka
joka perustui ohitettuihin alaluokkiin juosta()
. Kohdesuunnittelussa, johon jo liittyy perintö, tämä ei yksinkertaisesti toimi. Et voi samanaikaisesti periä RenderedObject
tai Tuotantolinja
tai MessageQueue
rinnalla Lanka
!
Tämä rajoitus vaikuttaa moniin Java-alueisiin, ei vain monisäikeisiin. Onneksi ongelmaan on olemassa klassinen ratkaisu Ajettava
käyttöliittymä. Kuten Jeff Friesen kertoi vuonna 2002 johdannossaan langoitukseen, Ajettava
käyttöliittymä on tehty tilanteisiin, joissa alaluokka Lanka
ei ole mahdollista:
Ajettava
käyttöliittymä ilmoittaa yhden menetelmän allekirjoituksen: void run ();
. Tämä allekirjoitus on identtinen Lanka
on juosta()
metodin allekirjoitus ja toimii ketjun suorituksen merkinnänä. Koska Ajettava
on käyttöliittymä, mikä tahansa luokka voi toteuttaa kyseisen käyttöliittymän liittämällä työvälineet
lauseke luokan otsikkoon ja antamalla sopiva juosta()
menetelmä. Suoritushetkellä ohjelmakoodi voi luoda objektin tai ajettava, kyseisestä luokasta ja välittää juoksevan henkilön viittauksen sopivaan Lanka
rakentaja. Joten niille luokille, jotka eivät voi laajentua Lanka
, sinun on luotava juokseva, jotta voit hyödyntää monisäikeisyyttä. Semanttisesti, jos teet järjestelmätason ohjelmointia ja luokkasi on is-suhteessa Lanka
, sinun pitäisi alaluokka suoraan Lanka
. Mutta suurin osa monisäikeisen sovellustason käytöstä perustuu koostumukseen ja määrittelee siten a Ajettava
yhteensopiva sovelluksen luokkakaavion kanssa. Onneksi koodaamiseen tarvitaan vain ylimääräinen rivi tai kaksi Ajettava
käyttöliittymä, kuten alla olevassa luettelossa 3 on esitetty.