Ohjelmointi

Moderni ketjuttaminen: Java-samanaikaisuusalusta

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 lankas - toisin sanoen konstruktoreiden kutsut ... uusi ExampleThread (tunniste, viive); - ja nokkela viives. Perusajatuksena on, että ohjelma käynnistyy kolme erillistä Lankas, 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 Lankas yhdessä näistä olosuhteista:

  1. 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ä.
  2. 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.
  3. 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 a Lankaliittyvä 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 Lankaon 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.

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