Hajautettujen sovellusten muodostamistekniikat (Java-kielellä), TKO_2014 Aineopinnot, syksy 2009 Turun yliopisto / Tietotekniikka

Koko: px
Aloita esitys sivulta:

Download "Hajautettujen sovellusten muodostamistekniikat (Java-kielellä), TKO_2014 Aineopinnot, syksy 2009 Turun yliopisto / Tietotekniikka"

Transkriptio

1 Hajautettujen sovellusten muodostamistekniikat (Java-kielellä), TKO_2014 Aineopinnot, syksy 2009 Turun yliopisto / Tietotekniikka c Ville Leppänen

2 This page is intentionally left blank.

3 Esipuhe Tämän oppimateriaalin tarkoituksena on opettaa kahteen kokonaisuuteen liittyviä asioita. Nämä kokonaisuudet ovat (1) verkon käyttöön perustuvien asiakas-palvelin sovellusten tekeminen niin asiakkaan kuin palvelimenkin osalta ja (2) säikeiden käytön hallinnan opettaminen. Näiden kahden kokonaisuuden alle mahtuu runsaasti yksityiskohtia ja ohjelmoinnin kannalta kokonaisuudet on jäsennetty useiksi rajapinnoiksi. Toisaalta tarkasteltavat kokonaisuudet ovat selkeästi osa nykyaikaista ohjelmointia. Oppimateriaali käsittelee edellisiä asioita lähinnä Java-kielen kautta tarkastellen erilaisia välikerrostekniikoita (middleware). Opettamisen kannalta konkretisointi jonkin kielen kautta on välttämätöntä, vaikka se osaltaan haittaakin kokonaisuuksiin liittyvien Javasta riippumattomien yleisten käsitteiden ja ratkaisumenetelmien tunnistamista JDK:n kirjastojen yksityiskohtien viidakosta. Useimmat kokonaisuudet esimerkiksi soketit ja XML ovat täysin Javasta riippumattomia, vaikka ne onkin jäsennetty JDK:n paketeiksi. JDK:n mielessä materiaali on kirjoitettu käyttäen enimmäkseen JDK 1.4:ää. Yleisesti ottaen lähes kaikki tässä käsitellyt asiat toimivat JDK:n versioissa Säikeiden osalta tässä esitettävä materiaali pohjautuu aikoinaan Timo Raidan kirjoittamaan materiaaliin. Säikeet ovat keskeisiä käytettäessä tietoverkkoa sovelluksissa. Erityisesti palvelinsovellukset nojautuvat säikeisiin hyvin voimakkaasti. Säikeitä käytetään myös GUI-pohjaisessa asiakassovelluksessa. Asioiden käsittely jatkossa perustuu pitkälti esimerkkien antamiseen käsiteltävistä aiheista. Luokkia (ja sitä kautta ohjelmia) materiaalissa esitetään noin 20 kappaletta. Joihinkin tarkasteltaviin asioihin liittyy varsin vankka teoria siitä, miten sovellukset kuuluisi rakentaan käyttämään tarkasteltavaa asiakokonaisuutta. Toisten kohdalla on vain hyvin runsaasti yksityiskohtia mutta vähemmän teoriaa soveltamisesta. Yksityiskohtien osalta materiaali ei pyri olemaan tyhjentävä lähinnä tarkoituksena niiden osalta on muodostaa lukijalle näkemys siitä, mitä kaikkea on tarjolla, miten eri konstruktioita tulisi käyttää ja mitä niillä voi saada aikaan. Lopuksi, verkon käyttäminen on hauska ja kenties jopa jännittävä asia oppia toivotan miellyttäviä ahaa-elämyksiä! Turussa, lokakuussa 2009, Ville Leppänen i

4 ii

5 Sisältö 1 Johdanto Internet-verkon taustaa Samanaikaisuuden taustaa Oppimateriaalin rakenne Säikeet 5 3 Säikeet (by Timo Raita) Perusteita Säikeen luonti ja käynnistys Säikeiden välinen kontrollin hallinta Resurssien jakaminen Säieryhmät ja demonit Johdatus hajautettuihin sovelluksiin Asiakas-palvelin sovelluksista yleensä Verkosta ja verkkoprotokollista Soketit ja niiden käyttö Perustietoa soketeista Luokka InetAddress Yhteydetön UDP Esimerkki UDP-paketin välityksestä Yhteydellinen TCP Palvelimen yleinen rakenne Esimerkki Finger Esimerkki olion välittäminen TCP-yhteyden yli Erikoisempia sokettien käyttötilanteita Ryhmälähetykset Appletit ja soketit RMI-pohjaiset sovellukset Yleinen tilanne ja käsitteitä Mitä RMI-kutsussa tapahtuu? Parametrien koodaus Etäolioon liittyvät luokat Esimerkkejä iii

6 iv SISÄLTÖ Vain yksi etäolio RMI:n käyttäminen käytännössä Useita etäolioita käyttävä esimerkki Yleisiä periaatteita RMI-sovellusten muodostamisesta RPC Taustaa Sovelluksen rakentaminen Esimerkki Huomioita ja yhteenveto Corba Taustaa Sovelluksen rakentaminen Esimerkki Mitä jäi kertomatta Verkkoprotokollat ja XML Verkkoprotokollat Taustaa Protokollan tilat asiakkaan ja palvelimen kannalta Protokollan muodostaminen ja kuvaaminen Protokollista luokiksi Uusia painotuksia: REST- ja ohut käyttöliittymä-tekniikat Johdatus XML:ään XML:n käyttötilanteista Tiedon esittäminen XML:llä DTD = Document Type Definition XML ja Java DOM:n luokista Jäsentäminen Xercesillä WWW ja servletit Johdanto Lomakkeista FORM:n määrityksistä Kenttä Valintalista Tekstialue Muita Servlettien tekemisessä tarvittavia luokkia Esimerkkejä Esimerkki HelloServlet Esimerkki hieman monimutkaisemmasta servletistä Lopuksi

7 SISÄLTÖ v 11 Tietokannoista Relaatiotietokannoista Mitä operaatioita kantaan kohdistettavissa? SQL-kielestä JDBC Yhteys verkkotietokantaan Komentojen suorittaminen Luokista MySQL esimerkki Lopuksi Dokumentointi Testaus Testausmenetelmiä Katsaus muihin tekniikkoihin ja suoritusalustoihin J2EE SOAP ja web-palvelut NET Remoting J2EE Virtuaalikoneet suoritusalustana Ohuet käyttöliittymät, SaaS ja pilvilaskenta Loppusanat 145

8 vi SISÄLTÖ

9 Luku 1 Johdanto Tämän oppimateriaalin tarkoituksena on johdatella lukija nykypäivän ohjelmointikieliin liittyviin moderneihin välikerrostekniikkoihin, joita käytetään hajautettujen sovellusten muodostamiseen. Kyseiset tekniikat eivät ole osa kieltä, vaan ne esiintyvät ohjelmointikielten yhteydessä kirjastojen kautta. Erityisesti tarkastellaan verkon olemassaoloon perustuvien asiakas-palvelin sovellusten tekemistä ja säikeiden kautta tutustutaan myös koneen prosessorivoiman parempaan hyödyntämiseen samanaikaisuuden 1 avulla ja 1970-luvuilla (ja laajasti myöhemminkin) ohjelmointi on tarkoittanut lähinnä keskusmuistissa olevan (ja tiedostoista keskusmuistiin tuodun) tiedon käsittelyä algoritmisesti. Ohjelmia käytettiin tuolloin eräajon kautta tai alkeellisen riviorientoituneen käyttöliittymän ((paperi)pääte) kautta. Tietokoneita käytettiin silloin lähinnä eräajosovelluksiin, kuten palkanlaskennan tekemiseen. Vuorovaikutteinen käyttö (ja sitä kautta graafinen käyttöliittymä) ja hajauteutut järjestelmät tulivat merkittävään rooliin vasta paljon myöhemmin. 1.1 Internet-verkon taustaa Nykyisen Internet-verkon kehityksen juuret ovat 1970-luvun ARPAnet hankkeessa (USAn puolustusvoimat). Kehitys oli aluksi aika hidasta: 1970-luvun lopussa oli jo useita erillisiä verkkoja ja ARPAnet:iinkin oli kytketty jo noin 200 konetta luku edusti räjähdysmäistä laajenemista ja kehitystä: viimeistään 1980-luvun lopussa 2 voitiin jo puhua maailmanlaajuisesta Internet-verkosta (jossa tuolloin oli jo noin konetta). Internet:ssä hyvin yleisesti käytetty TCP/IP-protokollien perhe muotoitui jo suhteellisen aikaisin: 1970-luvun loppupuolella. Nykyisinkin verkkosovellukset tyypillisesti hyödyntävät tämän protokollaperheen protokollia kirjastojen kautta. Ohjelmointikielten yhteyteen verkko tuli kirjastojen kautta lähinnä Unix:n kehityksen myötä. Unix:n ensimmäiset versiot olivat olemassa jo 1960-luvun lopussa (Ken Thompson). Monien kehitysvaiheiden jälkeen ARPAnet:n takana oleva organisaatio (DARPA) näki myös Unix:n mahdollisuudet ja niinpä ja 1980-lukujen taitteessa DARPA rahoitti verkko-ominaisuuksien liittämistä Unix:iin. Koska Unix oli toteutettu pääsääntöisesti C-kielellä, oli tällä konkrettinen seuraus C-kielen kirjastojen kannalta: verkon ja sen protokollien käytölle toteutettiin tuki C:n kirjastoihin. Voisi siis ajatella, että 1 Rinnakkaisuus tarkoittaa usean kontrollivirran suorittamista yht aikaa usean prosessorin toimesta. Samanaikaisuus on rinnakkaisuuden heikompi muoto, jossa vähintäänkin näennäisesti samaanaikaan suoritetaan useita käskyvirtoja käytännössä samanaikaisuus usein toteutetaan yhden laskentayksikön avulla lomittamalla jotenkin käskyvirtojen suoritusta. Javan säikeet perustuvat samanaikaisuuteen. Aidosti rinnakkaisuuteen perustuvia kieliäkin on niiden voima tyypillisesti perustuu suoritettavien käskyvirtojen tiukasti synkroniseen suoritukseen. Usein rinnakkaisuus saavutetaan ohjelmissa ilman rinnakkaisuuskonstruktioita kielen tasolla; esim. MPI-kirjaston avulla. 2 Verkossa jo vuodesta

10 2 LUKU 1. JOHDANTO verkot tulivat varsinaisesti osaksi ohjelmointia 1980-luvun alkupuolella luvulla syntyivät myös monet maailmanlaajuiset asiakas-palvelin sovelluskokonaisuudet, kuten esimerkiksi verkkouutiset. Verkkojen kehitys on jatkunut voimakkaasti 1980-luvun jälkeenkin ja on edelleenkin hyvin voimakasta. Syynä on lähinnä ollut WWW:n suosio ja viimeaikainen verkkojen yhtenäistymiskehitys. Yhtenäistymiskehitys on johtamassa siihen, että IP-pohjaiset verkot levittyvät (ja ovat jo osin levittäytyneet) kaikkialle: töihin, yhteiskunnan palveluorganisaatioihin, mobiililaitteisiin ja jopa kotiin (digitv, yms). Verkkojen merkitys osana ohjelmointia on selvästi noussut viime vuosina. 1.2 Samanaikaisuuden taustaa Rinnakkaisuuden (ja samanaikaisuuden) historia on myös hyvin tapahtumarikas 3. Rinnakkaisuutta on esiintynyt jo joissakin 1950-luvun lopun tietokoneissa, mutta varsinaiset ensimmäiset rinnakkaistietokoneet rakennettiin 1960-luvulla (Univac, CDC-sarjan koneet, ILLIAC IV). Rinnakkaiskoneiden ohjelmointia varten kehitettiin myös tukea korkean tason ohjelmointikieliin. Ensimmäiset tällaiset kielet olivat Fortran 66:n variaatioita, ja ne kehitettiin jo 1960-luvun lopussa. Sen jälkeen tukea koneen laskentavoiman mahdollisimman suureen hyödyntämiseen yhden ohjelman toimesta on kehitetty ohjelmointikieliin varsin eri tavoin. Moniajo tuli osaksi käyttöjärjestelmiä (esim. Unix) 1970-luvun puolivälissä ja sitä kautta ohjelmointikieliin (erityisesti C-kieli) tuli myös mahdollisuus käynnistää toisia säikeitä. Tämä edustaa samanaikaisuuden alkua. Rinnakkaisuuden toteuttaminen ja rinnakkaisuuden tukeminen ohjelmointikielen kannalta on kuitenkin vielä kehityslinja, joka ei ole ilmeisesti stabilisoitunut. Esim. Fortranin 1990-luvulla kehitetyt rinnakkaisuutta tukevat variaatiot ovat sellaisia, että niissä ei ohjelmointikielen tasolla ole edes prosessorin käsitettä (lähinnä rinnakkaisuus perustuu rinnakkaiseen silmukkaan ). Useita funktionaalisia kieliä on myös käytetty rinnakkaistietokoneiden ohjelmointiin. Viimeaikoina rinnakkaistietokoneiden ohjelmointi on usein perustunut jaetun muistin käsitteeseen (esim. Cray T3E tarjoaa tällaisen mahdollisuuden) ja/tai erilaisiin viestin välityskirjastoihin (MPI, PVM). Java ei ole rinnakkaisohjelmointikieli, vaan se tukee lähinnä samanaikaisuutta. Java-ohjelmia suoritetaan Java-virtuaalikoneen päällä (JVM, Java Virtual Machine). JVM tuntee säikeet ja eräissä toteutuksissa se jopa osaa käyttää useita prosessoreita ja käyttöjärjestelmätason säikeitä hyväkseen. Käyttöjärjestelmätason säikeiden (eli prosessien) ja Javan säikeiden välillä ei kuitenkaan ole mitään kiinteää 1:1-vastaavuutta. 1.3 Oppimateriaalin rakenne Ennen hajautettujen järjestelmien muodostamistekniikkojen tarkastelua tutustutaan ensin säikeisiin. Luvussa 3 kerrotaan säikeiden muodostamiseen ja käyttämiseen liittyvät yleisen asiat. Samalla pohditaan säikeisiin liittyvän samanaikaisuuden ja sen vaikutusten hallintaa. Javassahan on synchronized avainsana, jonka avulla voidaan estää säikeitä käsittelemästä esim. tiettyjä muuttujia samanaikaisesti. Vaikka säikeet eivät olekaan sama asia kuin rinnakkaisuus 4, niin tarkastelemalla samanaikaisuuteen 3 Gregory Wilson on koonnut verkkoonkin mielenkiintoisen historiikin (hae hakukoneella). 4 Rinnakkaisuus ei nykyään vielä ole osa perusohjelmointia. Kirjallisuudessa on kuitenkin esitetty hyvin perusteltuja kannanottoja sen puolesta, että ohjelmointia kuuluisi oikeastaan opettaa rinnakkaisohjelmointina peräkkäisohjelmoinnin sijaan. Syynä tähän on, että ongelmien ratkaisujen esittäminen rinnakkaisuuden avulla on luonnollisempaa kuin laskennan pakottaminen johonkin mahdollisista peräkkäisistä suoritusjärjestyksistä tällaisia järjestyksiähän on usein hyvin monia (mielivaltaisesti valittu peräkkäinen järjestys vaikeuttaa ohjelman merkityksen ymmärtämistä). Toinen rinnakkaisohjelmoinnin opettamista puoltava seikka on rinnakkaisuuden yleistyminen tietokoneissa. Varsin monissa tietokoneissa (ns. PC-koneissakin) on useita prosessoreita. Lisäksi (transistorien) pakkaustekniikka on kehittynyt viime aikoina huimasti:

11 1.3. OPPIMATERIAALIN RAKENNE 3 liittyvää problematiikkaa, tarkastellaan samantapaisia ongelmia kuin mitä liittyy aitoihin rinnakkaisohjelmointikieliin. Materiaali esitetään yhtenä lukuna, sillä se on edesmenneen Timo Raidan kirjoittamaa 5. Luvussa 2 summaan ne keskeiset asiat, joita lukijan tulisi oppia luvuista 3. Luvussa 4 teema vaihtuu säikeistä asiakas-palvelin sovelluksiin ja verkkoa hyödyntävään ohjelmointiin. Yleinen ja vanha tapa muodostaa asiakas-palvelin sovelluksia on käyttää C:stä (tai Unix:sta) tuttuja ns. soketteja. Tuolloin voi hyvin olla, että vain kommunikoinnin toinen osapuoli on Java-ohjelma. Verkkoyhteyden muodostamiseen sokettien avulla perehdytään luvussa 5. Sokettien avulla voidaan muodostaa lyhyt- ja pitkäkestoisia yhteyksiä. Pitkäkestoiset yhteydet perustuvat yhteydellisen TCP-protokolla käyttöön. Sokettien jälkeen luvussa 6 perehdytään Javan RMI-pakettiin, jonka avulla voidaan helposti muodostaa asiakas- ja palvelinsovellus, jotka kommunikoivat keskenään RMI:n välityksellä tietokoneverkon yli. Tässä tapauksessa kumpikin osapuoli on Java-ohjelma. RMI on lyhenne sanoista Remote Method Invocation, eli RMI on tapa kutsua etäällä toisen ohjelman muistissa (kenties toisessa koneessa) olevan olion metodia. RMI:tä vanhempi tapa kutsua toisen ohjelman muistiavaruudessa olevaa ohjelman osaa on RPCtekniikka (Remote Procedure Call). RPC:n lähtökohta on C-kielessä, vaikka siitä kyllä löytyy toteutuksia useille muillekin ohjelmointikielille Javalle ei suoraan ole toteutusta osana JDK:ta, mutta esim. XML-RPC:stä löytyy kyllä toteutus Javalle. RPC on toiminnaltaan ja toteutusperiaatteiltaan hyvin samanlainen kuin RMI. RPC:tä tarkastellaan luvussa 7. Historiallisessa mielessä RMI:tä edeltää myös Corba, jota tarkastellaan suppeasti luvussa 8. Corba on OMG (Object Management Group) luoma standardi (jolle löytyy useita toteutuksia; suppea toteutus on myös osa JDK:ta) etäolioiden määrittelemiseksi. Corba pyrkii olemaan RMI:stä poiketen yleinen, kieliriippumaton tapa tehdä hajautettuja järjestelmiä. Siihen liittyy myös tukea erilaisille korkeamman tason ominaisuuksille, kuten vikasietoisuudelle, kuormantasaukselle ja olioiden replikoinnille. RMI, RPC ja Corba kaikki rakentuvat TCP-sokettien päälle. Käytännössä ne määrittelevät tietynlaisen protokollan, jonka avulla kyseisen tekniikan mukaiset sovellukset voivat keskustella keskenään. TCP:tä käyttäville sovelluksille ominaista on, että osapuolten välinen kommunikaation etenee tietynlaista vuoropuhelua käyttäen tällöin usein ajatellaan, että protokolla on tietyssä tilassa. Protokollan tila-asioita käsitellään luvussa 9. Kyseisessä luvussa esitetään myös perusteet XML:n käyttämisestä Java-ohjelmien yhteydessä. Kun protokollalla välitetään tietoa sovelluksesta toiseen, välitettävä tieto pitää koodata jotenkin. XML soveltuu tällaiseen tiedon esittämiseen kohtuullisen hyvin. Java-ohjelmia käytetään verkossa varsin usein WWW:n yhteydessä. Appletteja voidaan käynnistää WWW-sivuilta ja selain osaa suorittaa niitä. Luvussa 10 käsitellään hieman suosittuun WWW:hen liittyviä asioita ja perehdytään erityisesti Javan servletteihin. Servletit ovat WWW-palvelimessa suoritettavia Java-ohjelmia, ja niitä voisi siten kutsua palvelinsovelmiksi. Vaikka edellisissä luvuissa onkin tarkasteltu palvelinpuolen toteuttamista itse ohjelmallisesti, niin usein palvelinsovelluksena viimekädessä toimii tietokanta. Luvussa 11 perehdytään lyhyesti tietokantojen käyttämiseen Java-ohjelmista. Luvuissa 12 ja 13 esitetään suppeasti huomioita hajautettujen järjestelmien dokumentoinnista ja testauksesta. Luvussa 14 puolestaan tehdään lyhyt katsaus muihin hajautettujen järjestelmien muo- Pentium-prosessorien toteuttaminen on vaatinut n miljoonaa transistoria, mutta pakkaustekniikan kehittyminen mahdollistaa moninkymmenkertaisen transistorimäärän (jopa miljardien) sijoittamisen yhden prosessorin alalle. Tätä ylimääräistä tilaa voi käyttää kahdella tavalla: muistina tai lisäämällä prosessorin rinnakkaisuutta. Uusimmat prosessorit sisältävät lähes poikkeuksetta rinnakkaista suoritusta tukevia piirteitä. 5 Kauan sitten itselläni oli Timo Raidan kanssa projekti usemman kurssin oppimateriaalin yhdistämiseksi isoksi kirjaksi, mutta sitä ei koskaan saatu vietyä loppuun asti.

12 4 LUKU 1. JOHDANTO dostamistekniikkoihin (erityisesti J2EE olisi saanut saada suuremman huomion) ja suoritusalustoihin. Loppusanat esitetään luvussa 15.

13 Luku 2 Säikeet Tämän luvun tarkoituksena on selvittää säikeisiin liittyvät käsitteet ja Java-kielen yhteydessä esiintyvät säikeisiin liittyvät konstruktiot ja luokat. Teoreettisen taustatarkastelun tarkoituksena on myös havaita säikeiden määrittelemän samanaikaisuuden mukanaan tuoma tiedon jakamiseen liittyvä problematiikka. Tämä problematiikka on Javan käyttämässä ratkaisussa, mutta Javan ratkaisuja ei kuitenkaan voi pitää huonoina, sillä samanaikaisuuden ja jakamisen problematiikka on hyvin syvällinen ja mitään universaalia ratkaisua ongelmiin ei kielen konstruktioiden avulla liene saavutettavissa (vaan ohjelmoijan täytyy ottaa vastuu tietynlaisten ongelmatilanteiden välttämisestä). Materiaalina käytetään Raidan Timon aikoinaan eri yhteyteen kirjoittamaa materiaalia sellaisenaan. Seuraavassa on lyhyesti lueteltu ne keskeiset seikat, jotka lukijan tulisi oppia Raidan Timon materiaalin (luku 3) perusteella. Säikeet ovat Javassa olioita. Ne luodaan tekemällä itse luokka, joka perii luokasta Thread tai rajapinnasta Runnable ja toteuttaa metodin run. Säikeen toiminnallisuus laitetaan run - metodiin, mutta itse run -metodia ei ohjelmassa tule suoraan kutsua, vaan säie-olion luonnin jälkeen start -metodin kutsuminen aiheuttaa itsenäisen säikeen käynnistymisen. Säikeisiin liittyy viisi tilaa: Uusi, Valmis, Suorituksessa, Estynyt ja Päättynyt. Tulee ymmärtää tilojen merkitys ja säikeiden siirtyminen tilasta toiseen (milloin, miksi ja mihin). Kun säikeen suoritus on päättynyt, niin kyseinen säie-olio on silti olemassa. Sitä ei voida enää käynnistää uudestaan, mutta säie-olion tietosisältöä voidaan toki havainnoida. Laskentaa suorittavien säikeiden kohdalla tämä antaa kätevän toimintatavan: Säikeen suorituksessa tuottamat tulokset talletetaan säie-olioon. Säikeisiin liittyvistä metodeista tulee ymmärtää erityisesti metodien sleep, yield, interrupt, join, wait, notify ja notifyall merkitys. Javan säikeitä suoritetaan samanaikaisesti ja mahdollisesti rinnakkain. Rinnakkaisuus tarkoittaa, että kahden tai useamman laskentayksikön (prosessorin) toimesta suoritetaan useita käskyvirtoja samalla ajan hetkellä. Samanaikaisuus on heikompaa : ohjelmassa on useita käskyvirtoja ja niiden kaikkien suoritus etenee, mutta samanaikaisuuden tapauksessa ei vaadita, että suorituksen pitäisi edetä samanaikaisesti tai samalla vauhdilla. Samanaikaisuuden tapauksessa voidaan ääritapauksessa toimia niin, että kaikkien säikeiden suoritus lomitetaan (jollakin tavalla) yhden prosessorin tehtäväksi. 5

14 6 LUKU 2. SÄIKEET Javan säikeitä suoritetaan JVM:n alaisuudessa ja siten JVM toimii tavallisen käyttöjärjestelmän tapaan antaen säikeille suoritusaikaa prosessorilta. Tätä varten säikeisiin liittyy prioriteetti, jonka mukaan JVM (ja yleisemmin käyttöjärjestelmä) kontrolloin säikeiden suoritukseen ottamista. Myös säikeiden tila vaikuttaa prioriteetin lisäksi siihen, mikä (tai mitkä) säie on milloinkin suorituksessa. Javan säikeet eivät vastaa käyttöjärjestelmätason säikeitä automaattisesti 1:1, vaan JVM:n toteutus viime kädessä määrää, voiko JVM käynnistää käyttöjärjestelmätason säikeen yhtä Javan säiettä kohti. Mahdollisesti JVM joutuu toteuttamaan yhden käyttöjärjestelmätason säikeen alaisuudessa kaikki Java-ohjelman säikeet. (Tämä asia on muutoksen alla, eikä sitä määritellä kielen tasolla.) Javan säikeiden pääasiallinen käyttötarkoitus ei liene lisätehon saaminen irti tietokoneesta (käyttämällä sen useita prosessoreita, jos niitä on), vaan pikemminkin tarkoituksena on mahdollistaa samanaikaisesti suoritettavien toimintojen ilmaiseminen helposti. Samanaikaisuus on hyödyllistä GUI-sovellusten yhteydessä (tapahtumankäsittelystä vastaa yksi säie) animaatioiden ja ympäristön tilan tarkkailun kannalta. Toisaalta säikeet ovat erityisen käteviä verkkosovellusten toteuttamisen yhteydessä. Javan tapauksessa säikeiden yhteistoiminta nojautuu lähinnä siihen, että ohjelmassa käynnistetyt säikeet jakavat saman muistiavaruuden eli ne pääsevät käsiksi samoihin olioihin. Tuolloin viestiminen yhdeltä säikeeltä toiselle tarkoittaa, että säie kirjoittaa jotakin yhteiseen muistiavaruuteen (johonkin muuttujaan tai olioon) ja toinen säie lukee tiedon kyseisestä paikasta. Javassa ei ole mitään komentoa tai kirjastossa olevaa metodia, jolla yksi säie voisi lähettää suoraan viestin toiselle säikeelle. Säikeiden yhteistoiminta on hieno asia ja muistiavaruuden jakaminen mahdollistaa sen ohjelmoijan kannalta suhteellisen vaivattomasti, mutta valitettavasti siihen liittyy erilaisia ongelmatilanteita. Olioiden (ja ylipäänsä) muistin jakamisessa täytyy ymmärtää siihen liittyvä problematiikka. Jos kaksi tai useampaa säiettä tekee päivityksiä samanaikaisesti yhteen muistialueeseen, esimerkiksi olion tietosisältöön, tulos saattaa hyvin olla jotakin muuta kuin mitä on saavutettavissa millään yksittäisellä säikeiden päivitystoimenpiteiden suoritusjärjestyksellä. Esimerkiksi jos pankkitili-olion sisältöön kohdistuu samaan aikaan otto- ja panotapahtuma. Saattaa toisen tapahtuman periaatteessa aiheuttama muutos tili-olion sisältöön jäädä kokonaan toteutumatta tuolloin koko järjestelmä voi mennä epäkonsistenttiin tilaan. Syynä tähän tapahtumaan on, että useasta yksittäisestä toimenpiteestä koostuvia operaatioita ei suoritetakaan atomaarisena toimenpiteenä, vaan kesken yhden toimenpidesarjan voi suoritus siirtyä toiseen säikeeseen, joka voi muuttaa samoja muuttujia kuin kesken jäänyt toimenpidesarjakin. Tietoresurssien jakamisen kontrolloimista varten Javassa on avainsana synchronized, jonka avulla voidaan lukita jokin olio niin, ettei usea säie voi samaan aikaan operoida kyseisellä oliolla. Synchronzied-määre voidaan liittää olio-arvoiseen lausekkeeseen, mutta myös jonkin metodiin. Kyseisen määreen kohtaava säie yrittää saada itselleen lukon kyseiseen olioon. Se saa lukon, jos jokin muu säie ei jo ole lukinnut kyseistä oliota. Muussa tapauksessa säie jää odottamaan, että lukon haltija vapauttaa lukon.

15 7 Edelliseen liittyy potentiaalinen ongelma. On tilanteita, jossa halutaan tehdä keskeytymätön toimenpidesarja, joka kohdistuu useaan olioon. Tuolloin säikeen tulee lukita kaikki kyseiset oliot ennen toimepidesarjan aloittamista. Tällainen usean olion peräkkäinen lukitseminen voi aiheuttaa sen, että säie on jo lukinnut tietyt oliot ja haluaa seuraavaksi lukita olion, joka on lukittu jo jonkun toisen säikeen toimesta. Tästä muodostuu ongelma, jos tämä toinen säie pyrkii lukitsemaan jotakin sellaista, joka on jo lukittu ensimmäisen säikeen toimesta. Yleisesti, lukitsemiset ja lukitsemisyritykset voivat muodostaa säikeiden kesken syklin. Tuolloin on syntynyt ns. lukkiumatilanne (deadlock), josta voidaan toipua vain vapauttamalla joitakin lukkoja. Valitettavasti, Javassa ei ole mitään automaattista menetelmää tällaisesta tilanteesta toipumiseksi, joten tilanteen välttäminen jää ohjelmoijan vastuulle. Toisenlainen häiriötilanne säikeiden suorittamisessa on, että jokin säikeen suoritus estyy koko ajan siitä syystä, että se ei saa lukittua kaikkia haluamiaan olioita samanaikaisesti. Tällaista tilannetta kutsutaan nälkiintymiseksi (starvation). Potentiaalisen lukkiumatilanteen estäminen siten, että jo muodostettuja lukkoja vapautetaan osittain, saattaa myös aiheuttaa ongelmatilanteen. On nimittäin mahdollista, että säikeet lukitsevat ja vapauttavat lukitsemiaan lukkoja (tilanteen ratkaisemiseksi) niin, että laskenta ei pääsekään etenemään (eli mikään säie ei saakaan lukko kaikkiin haluamiinsa kohteisiin siksi aikaa, että se voisi tehdä haluamansa toimenpidesarjan. Tällaista tilannetta kutsutaan eläväksi lukkiumaksi (livelock). Lukon voi olion lisäksi kohdistaa myös itse luokkaan, sen staattisiin piirteisiin. Säieryhmien ja demonien merkitys on myös selitetty luvun 3 materiaalissa. Luvussa 3 on hyvä esimerkki (luokka SharedData) siitä, miten tiedon jakaminen voidaan tehdä hallitusti ilman ongelmia. Esimerkki esittää kaksi synkronoitua jonoa jaettuun kommunikaatioon tuottajien ja kuluttajien välille (kumpiakin yksi per jono esimerkissä). Esimerkissä jonojen koko on radikaalisti supistettu äärimmäisyyteen, eli yhden alkion mittaiseksi. Huonona puolena esimerkissä voi pitää kahden rooliltaan erilaisen mutta sisällöltään samanlaisen jonon yhdistämistä yhdeksi luokaksi, kahden (yhtä jonoa esittävän luokan) instanssin sijaan. Tästä suunnittelupäätöksestä seuraa jopa se, että vaihtamalla esimerkissä olevat notifyall()-kutsut notify()-kutsuiksi, saa luokkaa SharedData käyttävät sovellukset deadlock-tilaan (mahdollisesti, kokeile!).

16 8 LUKU 2. SÄIKEET

17 Luku 3 Säikeet (by Timo Raita) 3.1 Perusteita Rinnakkaisuus on osa jokapäiväistä elämää, myös tietokoneiden kanssa peuhattaessa, halutaanhan koneella samanaikaisesti ladata verkosta materiaalia, tulostaa, lukea säköpostia jne. Säikeiden (threads) avulla Java-ohjelman eri toiminnot voidaan jakaa rinnakkaisesti suoritettaviin osiin. Näin matalan tason käsitteiden tuominen ohjelmointikieleen lisää sen ilmaisuvoimaa, mutta tuo samalla potentiaalisia vaaratilanteita, joita ei ole aina kovin helppo nähdä: lukkiintumisia, resurssien väärinkäyttöä jne. Tämän takia saattaisi olla parempi, että rinnakkaistaminen annettaisiin ajoaikaisen systeemin hoidettavaksi esimerkiksi kiinnittämällä kukin tunniste tiettyyn prosessoriin, jolloin kyseinen prosessori on vastuussa tunnisteen viitaamalle objektille tehtävistä toimenpiteistä 1. Koska Javan säikeet suoritetaan JVM:n alaisuudessa, niiden hallinta pitää hoitaa JVM:n sisällä. Tästä syystä säikeiden käsittely ei suoranaisesti liity allaolevan koneen käyttöjärjestelmän osaamistasoon, joskin JVM:n on helppo delegoida tehtävät sille, jos käyttöjärjestelmä tukee samoja toimintoja. Selvää tietysti on, että JVM on eräs käyttöjärjestelmän prosessi. Säikeiden käyttäytymistä voidaan kuvata kaikista käyttöjärjestelmäkirjoista tutulla transitiokaaviolla (kts. seuraavan sivun kuva), joka kertoo minkälaisissa tiloissa säie voi olla suorituksen aikana. Kun säie luodaan, se tulee tilaan Uusi. Säikeen käynnistys start-komennolla aiheuttaa sen siirtymisen tilaan Valmis. Kun säie saa prosessorin käyttöönsä, se siirtyy tilaan Suorituksessa, jossa säikeelle tehtyä run-rutiinia ajetaan. Tästä se voi siirtyä tilaan Päättynyt, jos sen suoritus saadaan loppuun tai nousee käsittelemätön keskytys, tilaan Valmis, jos se on käyttänyt aikaviipaleensa (time slice) loppuun, tai tilaan Estynyt, jos se odottaa IO-toiminnon loppumista, jää odottamaan signaalia toiselta säikeeltä tms. Koska ohjelman kirjoittajan on mahdotonta ennustaa miten säikeet aikaviipaleensa saavat ja käyttävät, rinnakkaisohjelmointi vaatii erityistä tarkkuutta, jotta objektit olisivat aina oikeassa tilassa silloin kun niitä tarvitaan. Tilassa Valmis ovat kaikki säikeet, jotka odottavat prosessoria käyttöönsä. Kun prosessori vapautuu, töidenjärjestelijän (scheduler) on valittava yksi säikeistä suoritukseen. Apunaan se käyttää taulua 1 Näin tehdään Eiffel-kielessä. Hämmästyttävää tässä mekanismissa on se, että kielen syntaksiin tarvitaan vain yksi uusi sana, separate, jolla tunnisteet leimataan. Ajoaikainen systeemi pitää tämän jälkeen huolen siitä, että prosessorit allokoidaan ja synkronointi hoidetaan järkevällä tavalla. 9

18 10 LUKU 3. SÄIKEET (BY TIMO RAITA) Suorituksen loppuminen normaalisti tai epänormaalisti Päättynyt (kuollut) Suorituksessa Uusi Säie valitaan suoritukseen Valmis Säie käynnistetään start komennolla Aikakvantti loppunut, yield, interrupt IO suoritettu, uniaika loppunut, notify, notifyall Säie luodaan IO pyyntö, sleep, wait Estynyt MAX_PRIORITY p1 p2 p3 MIN_PRIORITY p4 johon säikeet on järjestetty prioriteetin mukaan. Suuremman prioriteetin omaava säie suoritetaan aina ennen alemman prioriteetin omaavia 2. Prioriteettien minimiarvo on MIN_PRIORITY (1) ja maksimiarvo MAX_PRIORITY (10). Suorituksen alkaessa säikeellä on prioriteetti NORM_PRIORITY (5). Kun suorituksessa ollut säie on käyttänyt aikaviipaleensa tai siirtynyt tilaan Estynyt, prosessori vapautuu muiden käyttöön. Tällöin töidenjärjestelijä hakee taulukosta säikeen, jolla on suurin prioriteetti ja jos tällaisia on useita, valitaan se, jonka edellisestä aikaviipaleesta on kulunut kauimmin aikaa. Kuvan mukaisessa tilanteessa valitaan säie p1. Kun p1 vapauttaa prosessorin, se siirretään saman jonon hännille säikeen p3 perään (round robin -periaate) ja p2 otetaan suoritukseen. Alemman prioriteetin omaava p4 pääsee suoritukseen ainakin silloin,kun ylemmän prioriteetin omaavat on suoritettu loppuun tai ne kaikki ovat estyneitä. Järkevä töidenjärjestelypolitiikka tosin antaa sille aikaa silloinkin, kun ylemmän prioriteetin omaavat ovat suorituskelpoisia, mutta harvemmin. Jokaisella Java-ohjelmalla on ainakin yksi säie, nimittäin main. Se käsittelee tyypillisesti useita objekteja, joiden ohjaamana ohjelman kontrolli siirtyy paikasta toiseen. Tämän takia ohjelman suoritus on hyvin determinististä, ja me pystymme seuraamaan sitä käsky käskyltä. Tilanne muuttuu, kun rinnakkaisuus tulee mukaan, koska rinnakkaisprosessointi tuo ongelmia, joiden ratkaisut eivät aina 2 Jotta alemman prioriteetin omaavat tulisi suoritettua kohtuuajassa, niillekin pitäisi antaa prosessoriaikaa aina silloin tällöin. Töidenjärjestelijöiden toiminta vaihtelee eri JVM:issä. Joissakin säie suoritetaan aina loppuun (tai kunnes korkeamman prioriteetin omaava tulee suoritusvalmiiksi) ennen kuin saman tai alemman prioriteetin omaava otetaan käsittelyyn. Toisissa käytetään aikaviipaletta, jolloin saman prioriteetin omaavat pääsevät vuorottelemaan suorituksessa.

19 3.2. SÄIKEEN LUONTI JA KÄYNNISTYS 11 ole ilmeisiä. Syy tähän ei ole Javassa, vaan samanaikaisuudessa. Seuraavassa säikeiden toimintaa kuvataan tilakaavioon perustuen. Ensin tarkastellaan yksittäisen säikeen käynnistämistä ja lopettamista, sitten välineitä, joilla voidaan vaikuttaa kontrollin siirtymiseen säikeiden välillä ja lopuksi säikeiden välistä kommunikointia sekä säieryhmiä. 3.2 Säikeen luonti ja käynnistys Javassa on kaksi erikoisluokkaa säikeiden luontiin: rajapinta Runnable ja sen toteuttava konkreetti luokka Thread. Luokalla Runnable on vain yksi piirre, rutiini run, johon kirjoitetaan perijässä säikeen toiminta. Thread-luokkaan on koottu keskeiset säietoiminnot, joten me keskitymme seuraavassa niihin. Myöhemmin tarkastellaan luokkaa ThreadGroup, jonka avulla säikeitä voidaan niputtaa ja käsitellä siten yhtenä kokonaisuutena. Muita säikeisiin liittyviä luokkia ovat Object (josta löytyy rutiinit wait, notify ja notifyall), luokka ThreadLocal (jonka avulla saadaan käsiin säikeen sisäistä informaatiota) sekä poikkeusluokka ThreadDeath. Säikeen luonti tapahtuu normaaliin tapaan konstruktorin avulla. Kaikkein yleisin säiekonstruktori saa argumentikseen säieryhmän, johon säie halutaan liittää. Ryhmä on oletuksena sama kuin luontisäikeen ryhmä, Runnable-tyyppisen objektin, jos luokka ei halua periä konkreetilta luokalta Thread, ja säikeen nimen. Jos nimeä ei anneta, systeemi generoi automaattisesti nimen Thread-xx, missä xx on juokseva kokonaisluku. Muut konstruktorit ovat tämän yleisen muodon erikoistapauksia. Argumentit, joihin ei oteta kantaa, saavat oletusarvot. Luotavan säikeen prioriteetti on sama kuin luovan säikeen. Jos prioriteettia halutaan muuttaa, se voidaan tehdä rutiinilla setpriority. Vastaava havainnointioperaatio on getpriority. Myös säienimelle ja -ryhmälle on havainnointioperaatiot. Kun säie on luotu, sen suoritus alkaa start-kutsulla, jolloin säie siirtyy tilaan Valmis odottamaan prosessorin vapautumista. Kun se pääsee suoritukseen, se suorittaa run-rutiiniin kirjoitettua toimintoa. Katsotaanpa yksinkertaista esimerkkiä: public class ThreadTester public static void main( String args[] ) PrintThread säie1, säie2, säie3, säie4; säie1 = new PrintThread("Säie1"); säie2 = new PrintThread("Säie2"); säie3 = new PrintThread("Säie3"); säie4 = new PrintThread("Säie4"); System.err.println("\nSäikeiden käynnistys."); säie1.start(); säie2.start(); säie3.start(); säie4.start();

20 12 LUKU 3. SÄIKEET (BY TIMO RAITA) System.err.println( "Käynnistetty.\n"); class PrintThread extends Thread private int sleeptime; public PrintThread(String name) super(name); sleeptime = (int) (Math.random() * 5000 ); System.err.println( "Nimi: "+ getname() + "; Unessa: "+ sleeptime ); public void run() try System.err.println(getName() + "menee nukkumaan"); Thread.sleep(sleepTime); catch (InterruptedException exception) System.err.println( exception.tostring() ); System.err.println(getName() + "heräsi"); Rutiini main luo neljä säiettä ja käynnistää niiden suorituksen. Tämän tehtyään main jatkaa toimintaansa. Luonnin yhteydessä kullekin säikeelle arvotaan aika, jonka se nukkuu ja antaa siten muille säikeille mahdollisuuden päästä suoritukseen. Koska joku toinen voi herättää säikeen interruptkutsulla, sleep saattaa nostaa poikkeuksen InterruptedException, joten sen kutsu on sijoitettava try-lohkoon. Säikeiden run-toiminto ei ole kovin tyypillinen siinä mielessä, että se tulostaa vain merkkijonon ja lopettaa suorituksen. Normaalisti siinä suoritetaan silmukkaa, kunnes jokin ehto tulee täyteen. Ohjelman tulostus voisi näyttää vaikka seuraavalta: Nimi: Säie1; Unessa: 470 Nimi: Säie2; Unessa: 4677 Nimi: Säie3; Unessa: 1097 Nimi: Säie4; Unessa: 2419 Säikeiden käynnistys. Käynnistetty. Säie1 menee nukkumaan Säie2 menee nukkumaan Säie3 menee nukkumaan Säie4 menee nukkumaan Säie1 heräsi Säie3 heräsi Säie4 heräsi

21 3.2. SÄIKEEN LUONTI JA KÄYNNISTYS 13 Säie2 heräsi main-rutiinin herättämä start-kutsu aiheuttaa main-säikeen ja uuden säikeen suoritusten eroamisen omikseen (vrt. Unixin fork). main jatkaa suoritustaan välittömästi kutsun jälkeen ja kutsuttava siirtyy tilaan Valmis aloittaen run-rutiinin suorituksen siitä syystä, että start kutsuu runnimistä rutiinia. Thread-luokan run-toteutus ei tee oletuksena mitään, joten perijäluokan on aina syytä korvata se omallaan 3. Jos start kohdistetaan virheellisesti säikeeseen, joka on jo käynnissä, nousee poikkeus IllegalThreadStateException. Kun run on suoritettu loppuun, säie siirtyy kuolleeseen tilaan (dead state). Vaikka kuolleessa tilassa olevaa säiettä ei voi enää käynnistää, se on edelleen olemassa ja sen tilaa voi kysellä. Threadluokan isalive palauttaa totuusarvon true niin kauan kun säikeen run-rutiini on toiminnassa. Säie saa tietoa itsestään kutsumalla piirrettä currentthread, joka palauttaa Thread-tyyppisen objektin. Sen avulla säie saa selville esimerkiksi oman ryhmänsä, prioriteettinsa yms. Esimerkkimme main voisi antaa luomilleen säikeille vähän suuremman prioriteetin seuraavasti: Thread mythread = Thread.currentThread(); int mypriority = mythread.getpriority(); säie1.setpriority(mypriority+1); säie2.setpriority(mypriority+1); säie3.setpriority(mypriority+1); säie4.setpriority(mypriority+1); jolloin tuloksena on se, että luodut säikeet suorittavat toimintansa ja kuolevat ennen kuin main pääsee jatkamaan suoritustaan. Jotta kaikilla säikeillä olisi yhtäläiset mahdollisuudet päästä suoritukseen aika ajoin, pitää säikeiden siirtyä välillä odotustilaan. Tämä voidaan toteuttaa esimerkiksi kutsumalla sleep-proseduuria. Edellisessä esimerkissä säieluokka peri suoraan Thread-luokalta. Tämä on usein epäkäytännöllistä sen takia, että säie haluaisi pikemminkin periä joltain mielekkäämmältä, sen toimintaa mallintavalta luokalta ja Thread-luokalta periminen liittyy vain ajoaikaiseen käyttäytymiseen. Koska Javassa ei ole moniperintää, ongelma ratkaistaan rajapinnan Runnable avulla. Rajapinnan runoperaatiolla on tietysti tarkalleen sama signatuuri kuin Thread-luokassa (koska Thread on perinyt sen Runnable-rajapinnalta). Aiempaan esimerkkiin tulee tällöin vain kaksi muutosta: (a) PrintThreadluokan otsikko tulee muotoon class PrintThread implements Runnable ja (b) säikeen luonti tapahtuu main-rutiinissa kirjoittamalla Runnable alustus1 = new PrintThread(); Runnable alustus2 = new PrintThread(); Runnable alustus3 = new PrintThread(); Runnable alustus4 = new PrintThread(); Thread säie1 = new Thread(alustus1, "Säie1"); Thread säie2 = new Thread(alustus2, "Säie2"); Thread säie3 = new Thread(alustus3, "Säie3"); Thread säie4 = new Thread(alustus4, "Säie4"); Uuden Runnable-säikeen suoritusta ei voi aloittaa siis suoraan start-käskyllä, vaan sitä varten on luotava Thread-tyypin objekti, jonka konstruktorille Runnable-objekti annetaan argumenttina. Kun start aloitetaan, Thread-luokan run kutsuu konstruktorissa annetun Runnable-objektin run-rutiinia. Jos Runnable-objekti tarvitsee tietoa säikeestä, jossa se on, se voi kutsua funktiota currentthread. 3 Jos run on lyhyt ja se tehdään vain yhdelle säikeelle, Thread-luokan perijä voidaan kirjoittaa sisäluokaksi.

22 14 LUKU 3. SÄIKEET (BY TIMO RAITA) 3.3 Säikeiden välinen kontrollin hallinta Säie voi siirtyä suoritustilasta kolmeen eri tilaan: (a) Päättynyt: säie saadaan tehtyä loppuun tai se keskeytetään väkisin jonkin erikoistilanteen takia. (b) Valmis: säie on käyttänyt oman aikaviipaleensa ja luovuttaa prosessorin muiden käyttöön pakon (eli töidenjärjestelypolitiikan) sanelemana. Säie voi tehdä saman vapaaehtoisesti kutsumalla rutiinia yield tai interrupt. (c) Estynyt: säie suorittaa toimenpiteen, joka estää sen suorituksen väliaikaisesti. Kuvan mukaisesti tämä siirtymä tapahtuu silloin, kun (1) Säie odottaa syötön tai tulostuksen loppumista. (2) Säikeen suorituksessa kohdataan käsky sleep(ms) tai sleep(ms,ns), missä argumentti antaa millisekunteina (ja nanosekunteina) ajan, jonka säie on tilassa Estynyt. (3) Säikeen suorituksessa kohdataan jokin Object-luokasta löytyvän wait-käskyn muoto: wait(), wait(ms) tai wait(ms,ns). Tällöin säie pysyy tilassa Estynyt niin kauan, että jokin toinen säie herättää sen Object-luokan rutiinilla notify tai notifyall. (4) Säie yrittää kutsua toisen objektin synchronized-piirrettä ja joutuu odottamaan, koska kyseisen objektin lukko estää piirteen suorittamisen. (5) Säikeen suoritus on pysäytetty käskyllä suspend(). Suoritus jatkuu vasta, kun joku toinen kutsuu sitä resume-piirteellä 4. Jos säie haluaa itse vapaaehtoisesti poistua suorituksesta ja luovuttaa prosessorin muiden säikeiden käyttöön, se voi kutsua operaatiota yield. Itse asiassa on tietysti niin, että korkeamman prioriteetin omaava säie poistaa tällä hetkellä suorituksessa olevan säikeen automaattisesti, joten niitä varten ei voi tehdä yieldiä. Rutiinin yield semantiikan mukaan suoritusmahdollisuus annetaan korkeimman prioriteetin omaavalle suoritusvalmiille säikeelle. Jos yield-kutsun tekevä säie on ainoana omassa prioriteettiluokassaan, se tulee tällöin itse suoritukseen! Täten yield ei voi johtaa alempiprioriteettisten säikeiden suoritukseen. Ainoaksi mahdollisuudeksi jää se, että säie antaa suoritusmahdollisuuden vain saman prioriteetin omaaville säikeille. Aikaviipalesysteemissä tämä on tarpeetonta, joten rutiini on käyttökelpoinen vain niissä JVM:issä, jotka suorittavat säikeen aina kokonaan loppuun ennen uuden aloittamista. Mietitäänpä sitten hieman yleisellä tasolla niitä välineitä, joilla säikeet voivat kontrolloida toisiaan. Edellä puhutun perusteella on selvää, että säikeen suoritus voi erkautua toisesta rutiinin start kutsun johdosta ja ne voivat lähettää toisilleen signaaleja rutiinien notify, interrupt ja notifyall välityksellä. Jos säie on keskeyttänyt toimintansa oman wait-kutsun takia, se saadaan jatkamaan suoritustaan (eli siirtymään tilaan Valmis) joko notify- tai interrupt-kutsun seurauksena. Kutsun notify suorittava säie ei voi kuitenkaan kohdistaa herätystä mihinkään tiettyyn säikeeseen. Sen sijaan notifyall herättää kaikki odottavat säikeet. Rutiini wait ja objektin lukitseminen synchronized-määreellä selitetään tarkemmin seuraavassa kappaleessa. Säikeiden välinen toiminta saadaan synkronoitua rutiinilla join (tai join(ms), join(ms,ns)). Tällöin kutsun suorittava säie jää odottamaan kutsun kohteena olevan säikeen loppumista ja jatkaa eteenpäin vasta sen tapahduttua. Näin kutsuva säie voi varmistaa, että halutut toimenpiteet on tehty stop. 4 Näitä ei suositella enää käytettäväksi. Vanhentuneita ovat myös destroy (jota ei ole vielä edes implementoitu!) sekä

23 3.3. SÄIKEIDEN VÄLINEN KONTROLLIN HALLINTA 15 ennen suorituksen jatkamista. Jos join-rutiinille ei anneta argumenttia, kutsuja odottaa toisen loppumista maailman tappiin asti. Muussa tapauksessa kutsuja odottaa enintään argumentin ilmaiseman ajanjakson ja jatkaa sitten suoritustaan riippumatta siitä pääsikö kutsuttu säie loppuun vai ei. Katsotaanpa pari yksinkertaista esimerkkiä säikeiden synkronoinnista. Ensimmäinen toteutus käyttää luotujen säikeiden pollausta eli säikeet luonut main pitää kaikki langat käsisssään käyden läpi kaikki luomansa säikeet ja testaamalla niiden loppumista rutiinilla isalive. Aluksi säikeen toiminnan määrittelevä luokka, joka tulostaa ruudulle konstruktorille annetun välin [min, max] kokonaisluvut: class Counter implements Runnable public Counter(int min,int max) this.min = min; this.max = max; public void run() final int max = getmax(); for ( int i = getmin(); i <= max; i++) System.out.print(i + " "); try \\Annetaan muillekin säikeille tilaisuus. Thread.sleep(100); catch (InterruptedException e) System.out.println(); public int getmin() return min; public int getmax() return max; \\Range of numbers to be printed. private final int min, max; Seuraavaksi muodostetaan säieluokka Coordinator, joka luo ja kontrolloi kolmea Countersäiettä run-rutiinissa. Ennen alisäikeiden käynnistämistä Coordinator pienentää niiden prioriteettia, jotta se saisi käynnistettyä ne samanaikaisesti (silloin kun sen oma prioriteetti laskee riittävän alas): class Coordinator implements Runnable public Coordinator() initcounters(); protected void initcounters() Thread[] counters = getcounters(); for (int i = 0; i < counters.length; i++)

24 16 LUKU 3. SÄIKEET (BY TIMO RAITA) \\Annetaan kullekin luotavalle säikeelle oma toiminta-alueensa. final int min = 10*i+1, max = min+9; counters[i] = new Thread(new Counter(min,max)); System.out.println("Säikeet luotu."); public void run() Thread mythread = Thread.currentThread(); int mypriority = mythread.getpriority(); Thread[] counters = getcounters(); for (int i = 0; i < counters.length; i++) \\Lasketaan säikeiden prioriteettia, jotta ne eivät lähtisi \\käyntiin ennen aikojaan. counters[i].setpriority(mypriority-1); counters[i].start(); System.out.println("Kaikki valmiina."); waitforcounterstofinish(mypriority); System.out.println("Kaikki säikeet ovat lopettaneet."); protected void waitforcounterstofinish(int mypriority) \\Lasketaan omaa prioriteettia, jotta alisäikeet pääsevät suoritukseen. Thread mythread = Thread.currentThread(); mythread.setpriority(mypriority-1); do try Thread.sleep(500); catch (InterruptedException e) while (stillrunning()); protected boolean stillrunning() \\Loppuehto: Palauttaa arvon true, jos yksikin säie on käynnissä. Thread[] counters = getcounters(); for (int i = 0; i < counters.length; i++) if (counters[i].isalive()) return true; return false; protected Thread[] getcounters() return counters; private final int numcounters = 3;

25 3.3. SÄIKEIDEN VÄLINEN KONTROLLIN HALLINTA 17 private final Thread[] counters = new Thread[numCounters]; Tämän jälkeen tarvitaan vielä main, joka luo Coordinator-tyyppisen säikeen. Ohjelman tulostus näyttää seuraavalta: Säikeet luotu. Kaikki valmiina Kaikki säikeet ovat lopettaneet. Counter-säikeet generoiva säie alentaa omaa prioriteettiaan, jotta kolme luotua säiettä saisivat vapaasti kilpailla keskenään. Kovin paljon kilpailua ei kuitenkaan synny, koska alimman tason säikeet nukkuvat aina saman verran yhden luvun tulostettuaan. Nukkumisaika on pitkä verrattuna tulostamiseen, joten säikeet siirtyvät vuorollaan aina ko. prioriteettitason jonon hännille nukkumisjakson päätyttyä, josta ne sitten otetaan suoritukseen round robin -periaatteella. Tulostus muuttuu tietysti aika lailla, jos sleep-rutiinin argumentiksi annetaan vaikkapa max, koska silloin alkupään numerot tulevat tulostettua loppupään numeroita nopeammin. Alisäikeiden loppumista testataan rutiinissa waitforcounterstofinish silmukan sisällä. Vaihtoehtoisesti systeemi voitaisiin rakentaa sellaiseksi, että alisäikeet ilmoittavat luojalleen oman toimintansa loppumisesta. Ainoa ero edelliseen toteutukseen on, että (stillrunning poistetaan ja) waitforcounterstofinish käyttää join-operaatiota: protected void waitforcounterstofinish(int mypriority) Thread mythread = Thread.currentThread(); mythread.setpriority(mypriority-1); Thread[] counters = getcounters(); for (int i = 0; i < counters.length; i++) \\Anticipate being interrupted before the join is complete. boolean interrupted; do try interrupted = false; counters[i].join(); catch (InterruptedException e) interrupted = true; while(interrupted); Nyt alisäikeet luonut Coordinator odottaa join-operaatiossa, kunnes kyseinen alisäie on lopettanut toimintansa. Toteutuksessa tarvitaan kaksinkertainen silmukka: ulompi käy läpi kaikki alisäikeet ja sisempää tarvitaan sen takia, että join-operaation odotus voi keskeytyä muustakin syystä kuin odotettavan säikeen loppumisesta. Samasta syystä tarvitaan myös try-lohkoa. Jos alisäie on jo lopettanut toimintansa tultaessa join-kutsuun, jatketaan ohjelman suoritusta välittömästi. Threadluokan muut join-variaatiot ovat turvallisempia kuin ylläkäytetty, siinä mielessä, että ne odottavat vain argumenttina annetun ajan ja jatkavat sitten suoritustaan. Tämä on hyödyllistä, koska säie, jonka loppumista odotetaan voi odottaa puolestaan itse jotain (jopa epäsuorasti omaa loppumistaan), pahimmassa tapauksessa loputtomiin.

26 18 LUKU 3. SÄIKEET (BY TIMO RAITA) 3.4 Resurssien jakaminen Säikeet toimivat normaalisti yhteisen päämäärän hyväksi ja tähän pyrkiessään niiden pitää aina silloin tällöin kommunikoida keskenään. Tavallinen kommunikointitapa on käyttää yhteistä muistialuetta, johon säikeet kirjoittavat ja josta ne lukevat tietoa. Tällainen tilanne saadaan helposti aikaan luomalla ohjelmassa ensin muistialue, jonka kautta kommunikointi tapahtuu. Tämän jälkeen säikeille välitetään tieto yhteisesta alueesta antamalla siihen viittaus, jonka säikeet voivat tallettaa omiin sisäisiin attribuutteihinsa myöhempää käyttöä varten. Yleisesti ottaen kirjoittavia ja lukevia säikeitä voi olla useita. Jos säikeillä on tiukasti rajatut roolit siinä mielessä, että tietty säie voi vain lukea tai kirjoittaa, mutta ei tee koskaan molempia, ohjelmoija pääsee helpommalla, sillä synkronointi tehdään hänen puolestaan, jos hän käyttää putkea. Tarkastellaan tapausta, jossa kirjoittajia on kaksi ja lukijoita on vain yksi. Toinen kirjoittavista säikeistä on PrimeMaker, joka generoi alkulukuja ja kirjoittaa ne putkeen: import java.io.*; class PrimeMaker extends Thread private DataOutputStream out; public PrimeMaker(DataOutputStream o) out = o; public void run() int newvalue = 1; try while (newvalue < 10000) newvalue = newvalue + 1; boolean isprime = true; for ( int i=2; i*i <= newvalue; i++) if (newvalue % i == 0) isprime = false; break; if (isprime) System.out.println("writing new prime "+ newvalue); out.writeint(newvalue); out.close(); catch (IOException e) return; FibMaker on periaatteeltaan aivan identtinen, mutta generoi Fibonaccin lukuja: import java.io.*; class FibMaker extends Thread private DataOutputStream out; public FibMaker(DataOutputStream o) out = o;

27 3.4. RESURSSIEN JAKAMINEN 19 public void run() int n=0; int m=1; try out.writeint(m); while (m < 10000) int newvalue = n+m; n = m; m = newvalue; System.out.println("writing new Fibo "+ newvalue); out.writeint(newvalue); out.close(); catch (IOException e) return; Koko hommaa kontrolloi luokka PipeReader. Luokka luo putket ja käynnistää sitten PrimeMakerja FibMaker-tyyppiset säikeet, jotka generoivat kokonaislukuja: PrimeMaker FibMaker PipeReader PipeReader etsii lukuvirrasta duplikaatit (tietäen, että kumpikin lähde tuottaa niitä nousevassa suuruusjärjestyksessä) ja tulostaa ne: import java.io.*; public class PipeReader static public void main(string[] args) PipeReader world = new PipeReader(System.out); private PipeReader(PrintStream out) DataInputStream fibs = makefibs(); DataInputStream primes = makeprimes(); try int x = fibs.readint(); int y = primes.readint(); while (x < 10000) if (x == y) out.println("integer "+ x + "is both fib and prime");

28 20 LUKU 3. SÄIKEET (BY TIMO RAITA) x = fibs.readint(); y = primes.readint(); else if (x < y) x = fibs.readint(); else y = primes.readint(); catch (IOException e) System.exit(0); private DataInputStream makefibs() try PipedInputStream in = new PipedInputStream(); PipedOutputStream out = new PipedOutputStream(in); Thread fibthread = new FibMaker(new DataOutputStream(out)); fibthread.start(); return new DataInputStream(in); catch (IOException e) return null; private DataInputStream makeprimes() try PipedInputStream in = new PipedInputStream(); PipedOutputStream out = new PipedOutputStream(in); Thread primethread = new PrimeMaker(new DataOutputStream(out)); primethread.start(); return new DataInputStream(in); catch (IOException e) return null; Ohjelman tulostus näyttää seuraavalta (ylimääräisiä rivejä poistettu): writing new Fibo 1 writing new Fibo 2 writing new Fibo 3 writing new Fibo 5 writing new Fibo 8. writing new prime 2 writing new prime 3 writing new prime 5 writing new prime 7. Integer 2 is both fib and prime Integer 3 is both fib and prime Integer 5 is both fib and prime Integer 13 is both fib and prime. Tämä on esimerkki yleisestä tilanteesta, jossa säikeet yrittävät päästä samanaikaisesti käsiksi samoihin tietoihin. Nämä säikeet olivat kuitenkin toisistaan riippumattomia siinä mielessä, että ne eivät

29 3.4. RESURSSIEN JAKAMINEN 21 tienneet olevansa toistensa kanssa tekemisissä, eikä niiden näin ollen myöskään tarvinnut synkronoida toimintojaan millään tavalla (ja sekin mikä tarvittiin, tarjottiin ilmaiseksi putken avulla). OOsysteemi koostuu kuitenkin toistensa kanssa vuorovaikutuksessa olevista objekteista ja jos ne ovat eri säikeissä, pitää säikeiden toimintaa synkronoida jollakin tavalla. Synkronointia tarvitaan aina resurssien jakamisen takia. Resurssi on jokin ohjelman ympäristöstä löytyvä tarpeellinen väline: keskusmuistialue, objektijoukko, IO-laite, tietokanta jne. Jos edellisessä esimnerkissä haluttaisiin itse kontrolloida tarkemmin tapahtumia ja käyttää tiedon välitykseen vain kahta erillistä int-muuttujaa, kirjoittaminen ja lukeminen pitää synkronoida, jotta lukija ei lue samaa tietoa useaan eri otteeseen ja kirjoittaja ei kirjoita uutta tietoa vanhan päälle ennen kuin lukija on ehtinyt lukea aiemman. Tällaisesta tilanteesta käytetään nimitystä kilpailutilanne (race condition, race hazard) ja se on hyvin yleinen monessa tilanteessa (esimerkiksi luvun lipunvarausjärjestelmässä useasta eri konttorista voidaan yrittää varata sama istumapaikka). Java käyttää synkronointiin C.A.R. Hoaren vuonna 1974 esittelemää monitori-käsitettä (monitor). Ajatuksena on etsiä koodista nk. kriittiset osat (critical sections), joiden aikana toinen säie ei saa keskeyttää suoritusta. Javassa on kaksi tapaa merkitä kriittinen osa. Ensiksi tarkasteltavassa tavassa yksittäinen rutiini on yksikkö, joka voidaan leimata haluttaessa kriittiseksi (myöhemmin esitellään tapa, jolla kriittiseksi osaksi voidaan valita mielivaltainen lauselohko). Rutiini on kriittinen, jos se on varustettu synchronized-määreellä. Jokainen objekti, johon liittyy ainakin yksi synkronoitu rutiini, on puolestaan monitori. Monitoriobjektille on ominaista, että siihen voidaan kohdistaa vain yksi synkronoitu operaatio kerrallaan (muut säikeet voivat kyllä suorittaa samanaikaisesti ei-synkronoituja rutiineja). Suorituksessa olevalla säikeen sanotaan tällöin lukitsevan (lock) objektin. Kun synkronoitua rutiinia kutsutaan, JVM tarkistaa onko monitori lukittu. Jos on, asiakkaan kutsu laitetaan jonoon niiden kutsujen perään, jotka odottavat lukon vapautumista. Jos ei, asiakas saa lukon ja pystyy jatkamaan suoritustaan. Kun synkronoidun rutiinin runko on suoritettu loppuun, lukko luovutetaan muiden käyttöön ja korkeimman prioriteetin omaava odottava säie pääsee suoritukseen (ja saa lukon itselleen). Joissakin tilanteissa lukon omaava säie voi todeta, että se ei voi edetä suorituksessa ja se kutsuu rutiinia wait. Tällöin säie siirtyy odotustilaan (tilan Estynyt erikoistapaus) ja vapauttaa lukon. Säie voi kutsua synkronoidun rutiinin lopussa piirrettä notify, joka vapauttaa yhden odotustilassa olevista säikeistä ja siirtää sen tilaan Valmis. Rutiinin notify kutsua ei voi kohdistaa mihinkään tiettyyn säikeeseen, vaan systeemi valitsee jonkin odottavista korkeimman prioriteetin omaavista säikeistä. Jos lukon omaava piirre kutsuu rutiinia notifyall, kaikki odottavat säikeet siirretään tilaan Valmis, joskin vain yksi niistä saa itselleen lukon. On huomattava pieni, mutta selkeä ero niiden säikeiden välillä, jotka ovat joutuneet odotustilaan wait-käskyn ansiosta ja niiden, jotka ovat jonottamassa lukkoa. Kun lukko vapautuu, jälkimmäisistä joku valitaan suoritukseen, mutta edelliset voivat päästä suoritukseen vasta, kun joku toinen säie kutsuu rutiinia notify tai notifyall. Koska wait, notify ja notifyall on sijoitettu luokkaan Object, kaikilla objekteilla on mahdollisuus tulla monitoriksi. Ratkaistaan edellinen Fibonaccin lukuja ja alkulukuja ksäittelevä systeemi nyt synkronointia käyttäen. Tätä varten muodostetaan ensin luokka, jossa on omat private-tyyppiset kokonaislukuattribuuttinsa kumpaakin varten. Molemmille on myös get- ja set-operaationsa, joiden pitää olla synkronoituja. Luvun asettavat set-rutiinit tarkistavat totuusarvoisen muuttujan avulla, onko edellinen luku jo luettu attribuutista. Jos on, uusi luku voidaan asettaa attribuuttiin. Muussa tapauksessa säie jää odottamaan tehtyään wait-kutsun. Odotus katkeaa siinä vaiheessa, kun lukeva säie antaa notify-kutsun tiedon luettuaan. Vastaava toiminto on myös get-rutiineissa: public class SharedData private int sharefib = 0;

30 22 LUKU 3. SÄIKEET (BY TIMO RAITA) private int shareprime = 0; private boolean writeablefib = true; private boolean writeableprime = true; public synchronized void setfib(int newvalue) while (!writeablefib) try wait(); catch (InterruptedException e) e.printstacktrace(); System.out.println(Thread.currentThread().getName() + "setting sharefib to "+ newvalue); sharefib = newvalue; writeablefib = false; notify(); public synchronized void setprime(int newvalue) while (!writeableprime) try wait(); catch (InterruptedException e) e.printstacktrace(); System.out.println(Thread.currentThread().getName() + "setting shareprime to "+ newvalue); shareprime = newvalue; writeableprime = false; notify(); public synchronized int getfib() while (writeablefib) try wait(); catch (InterruptedException e) e.printstacktrace(); writeablefib = true; notify(); System.out.println(Thread.currentThread().getName() + "retrieving sharefib value "+ sharefib); return sharefib; public synchronized int getprime() while (writeableprime)

31 3.4. RESURSSIEN JAKAMINEN 23 try wait(); catch (InterruptedException e) e.printstacktrace(); writeableprime = true; notify(); System.out.println(Thread.currentThread().getName() + "retrieving shareprime value "+ shareprime); return shareprime; Huomaa kussakin synkronoidussa rutiinissa käytetty while-rakenne. Kun säie poistuu odotustilasta, ei ole tietenkään mitään takeita siitä, että silmukkaehto olisi edelleen voimassa. Tämä johtuu siitä, että (a) joku muu säie on voinut muuttaa ehtolausekkeen arvoa odotusaikana, ja (b) odotus voi loppua muuhunkin kuin juuri kyseiselle säikeelle kohdistetusta notify-operaatiosta. Kirjoittava säie käyttää rutiineja aivan normaalisti: public class FibThread extends Thread private SharedData handle; public FibThread(SharedData sd) super("fibthread"); handle = sd; public void run() int n=0, m=1; handle.setfib(m); while (m < 1000) int newvalue = n+m; n = m; m = newvalue; handle.setfib(newvalue); return; Luokka PrimeThread toimii vastaavasti. Lukeva säie näyttää seuraavalta: public class ReaderThread extends Thread private SharedData handle; ReaderThread(SharedData sd) super("readerthread"); handle = sd; public void run() int x = handle.getfib(); int y = handle.getprime(); while (x < 1000)

32 24 LUKU 3. SÄIKEET (BY TIMO RAITA) if (x == y) System.out.println("Integer "+ x + "is both fib and prime"); x = handle.getfib(); y = handle.getprime(); else if (x < y) x = handle.getfib(); else y = handle.getprime(); ja ohjelman tulostus seuraavalta: PrimeThread setting shareprime to 2 FibThread setting sharefib to 1 ReaderThread retrieving sharefib value 1 ReaderThread retrieving shareprime value 2 FibThread setting sharefib to 1 ReaderThread retrieving sharefib value 1 PrimeThread setting shareprime to 3 FibThread setting sharefib to 2 ReaderThread retrieving sharefib value 2 Integer 2 is both fib and prime FibThread setting sharefib to 3 ReaderThread retrieving sharefib value 3 FibThread setting sharefib to 5 ReaderThread retrieving shareprime value 3 Integer 3 is both fib and prime PrimeThread setting shareprime to 5 ReaderThread retrieving sharefib value 5 FibThread setting sharefib to 8 ReaderThread retrieving shareprime value 5 Integer 5 is both fib and prime. Tämä toteutus antaa ratkaisun tilanteelle, jossa kirjoittaja ja lukija toimivat tasatahtiin: kirjoittaja asettaa arvon, lukija lukee sen ja tämä toistuu kunnes kaikki tiedot on generoitu ja luettu. Yleisessä tapauksessa ei ole kuitenkaan mukavaa, että kirjoittaja ja lukija on sidottu toisiinsa näin voimakkaasti. Tällöin voidaan käyttää putkia tai yleistää ylläoleva ratkaisu omaan puskurialueen hallintaan. Attribuuttia ei voi esitellä synkronoiduksi. Tässä on taas yksi lisäsyy määritellä attribuutit privatetyyppisiksi ja kirjoittaa niille järkevillä suojaus- ja synkronointimääreillä varustetut get- ja setoperaatiot. Attribuuttiin voidaan liittää määre volatile, joka kertoo Java-kääntäjälle, että attribuutin käsittelyyn liittyvää koodia ei kannata optimoida viimeiseen asti. Erityisesti sillä halutaan kertoa, että ko. attribuutin arvoa ei ole syytä pitää suoritusaikana rekisterissä sinä aikana kun muistipaikan arvoa käsitellään, koska kyseinen arvo saattaa muuttua. Määre volatile siis tarkoittaa sitä, että attribuutin käsittely alkaa aina tiedon lataamisella muistipaikasta, ei rekisteristä. Jos kuitenkin noudatat ylläesiteltyjä sääntöjä attribuuttien suojauksesta ja merkitset niitä käsittelevät rutiinit synkronoiduiksi, et tarvitse volatile-attribuutteja. Monitorit eivät ratkaise kaikkia reaaliaikaohjelmoinnissa esiintulevia ongelmia, mutta auttavat tapahtumien synkronoinnissa. Tärkeätä on huomata, että synkronoitua rutiinia suorittava säie ei saa pysäyttää toimintaansa esimerkiksi sleep-käskyllä, koska sleep ei anna lukkoa muiden käyttöön,

33 3.4. RESURSSIEN JAKAMINEN 25 ainoastaan wait tekee sen (toisaalta, wait-käskyä saa käyttää vain synkronoidun rutiinin tai lohkon sisällä, sleep-käskyä missä tahansa). Jos siis haluat estää lukkiutumiset, käytä monitorin sisällä aina wait-käskyä silloin, kun pysähtyminen on tarpeen. Sitäpaitsi vaikka kaikkia esitettyjä ohjeita noudatettaisiin, virhetilanteita saattaa syntyä silti. Tarkastellaanpa säikeitä, jotka on kirjoitettu pankkitilin käsittelyyn. Tyypillisiä tapahtumia ovat otto automaatilta, tilisiirto, suoraveloitus jne., jotka kaikki voivat tapahtua samaan aikaan. Ensimmäinen toimenpide on tietysti tehdä tililtä nostava rutiini nosto ja tilikyselyrutiini saldo synkronoiduiksi, koska muuten useat eri säikeet voivat kysellä saldoa samanaikaisesti ja nostaa samat rahat tililtä moneen kertaan. Mutta tämäkään ei riitä. Vaikka kukin säie saakin monitorin yksin haltuunsa näitä rutiineja suorittaessaan, voi rutiinikutsujen saldo ja nosto välillä tapahtua kuitenkin muita tilitapahtumia. Esimerkiksi pankkiautomaattia mallintavassa säikeessä tehdyn saldotarkistuksen jälkeen kyseisen säikeen suoritus saatetaan keskeyttää suoraveloitussäikeen ansiosta ja jälkimmäinen tekee veloituksen. Tämän jälkeen automaattiottosäie saa taas kontrollin ja yrittää nostaa annetun rahamäärän (joka alkujaan todettiin pienemmäksi kuin saldo), mutta ei onnistu, koska suoraveloitussäie on vetänyt välistä. Ongelma saadaan ratkaistua, mikäli meillä on mahdollisuus lukita koko tiliobjekti siksi ajaksi kun joku on pankkiautomaatilla. Tätä varten Javassa on käytettävissä nk. synkronoitu lohko (synchronized statement), joka on muotoa synchronized (lauseke) /* Käskyt, joiden aikana lukitus on päällä */ Normaalisti lauseke on viittaus siihen objektiin, joka halutaan lukita, mutta yleisessä tapauksessa se voi olla lauseke, joka palauttaa objektiviittauksen. Lukitsemalla koko tiliobjekti saldon ja noston ajaksi toiminnasta saadaan atomaarinen siinä mielessä, että muut säikeet eivät pääse väliin vaan joutuvat odottamaan lukon vapautumista. Nyt kun tämäkin ongelma on saatu ratkaistua, muistutetaan vielä siitä, että reaaliaikaohjelmoinnissa täytyy ottaa vielä muitakin asioita huomioon: automaatilla oleva käyttäjä voi tumpuloida pahasti, tietoliikenneyhteydet voivat mennä poikki jne. Automaattiottosäikeen täytyy kuitenkin pystyä näissäkin tilanteissa vapauttamaan lukko, koska muuten koko systeemi lukkiutuu (deadlock). Ja vielä muutama huomio säikeiden ja synkronoinnin käytöstä. Synkronoitu lauseke mahdollistaa yleisesti tavattavan, muotoa public synchronized void method() \\Ei-kriittinen osa; \\Kriittinen osa; \\Ei-kriittinen osa; olevan rutiinin muuttamisen muotoon public void method() \\Ei-kriittinen osa; synchronized(this) /* Kriittinen osa; */ \\Ei-kriittinen osa; Luokan konstruktoria ei tarvitse esitellä synkronoiduiksi, koska objektin luova säie on aina yksinään vastuussa luontioperaatiosta. Jos synkronoitu rutiini kutsuu toista synkronoitua rutiinia, säikeen ei tarvitse saada lukkoa toistamiseen (eihän siitä ole kuin yksi esiintymä olemassa!) vaan voi jatkaa kutsumaansa piirrettä normaalisti. Yleisperiaatteena voidaan siis sanoa, että lukon omaavalla säikeellä on oikeus käsitellä objektia kuinka haluaa muiden häiritsemättä. synchronized-määreeseen ei liity samanlaisia sääntöjä kuin suojausmääreisiin. Erityisesti jos isäluokka esittelee synkronoidun rutiinin

34 26 LUKU 3. SÄIKEET (BY TIMO RAITA) ja perijä antaa sille korvauksen, jälkimmäisen ei tarvitse olla synkronoitu. Jos korvaava toteutus kuitenkin käyttää isäluokan versiota toimintansa osana, se on synkronoitu ko. rutiinin suorituksen ajan. Luokalla on myös oma lukkonsa, joten synchronized static -määreillä leimatut operaatiot voivat estää muiden samanaikaisen pääsyn static-tyyppisiin luokkatietoihin. 3.5 Säieryhmät ja demonit Kaikki säikeet liitetään luonnin yhteydessä johonkin säieryhmään (thread group). Oletuksena säie liitetään siihen ryhmään, johon sen luova säiekin kuuluu. Säie main kuuluu säieluokkaan main. Säieryhmä voidaan luoda ja nimetä kutsumalla luokan ThreadGroup konstruktoria: ThreadGroup folders = new ThreadGroup( database ); Uusi ryhmä kiinnitetään oletuksena sen ryhmän lapseksi, missä ko. ryhmän luova säie on. Isäluokka voidaan antaa myös eksplisiittisesti argumenttina onstruktorille. Ryhmät muodostavat siis puurakenteen, jonka juurena on main. Niputtamalla luodut lapsisäikeet samaan ryhmään niitä voidaan käsitellä yhtenä kokonaisuutena: ryhmän kaikkien säikeiden suoritus voidaan keskeyttää, maksimiprioriteettia muuttaa, asettaa demoneiksi jne. Tämän nojalla aiemman Coordinator-luokan waitforcounterstofinish voidaan kirjoittaa uudelleen niin, että se testaa lapsisäikeiden muodostaman ryhmän kokoa: class GroupCoordinator implements Runnable public GroupCoordinator() initcounters(); protected void initcounters() Thread[] counters = getcounters(); ThreadGroup group = getgroup(); for (int i = 0; i < counters.length; i++) final int min = 10*i+1, max = min+9; counters[i] = new Thread(group, new Counter(min,max)); System.out.println("All counters created."); public void run() Thread[] counters = getcounters(); for ( int i = 0; i < counters.length; i++) counters[i].start(); System.out.println("All counters ready to run."); waitforcounterstofinish(); System.out.println("All the threads have finished."); protected void waitforcounterstofinish() ThreadGroup group = getgroup();

35 3.5. SÄIERYHMÄT JA DEMONIT 27 do try Thread.sleep(500); catch (InterruptedException e) while (group.activecount() > 0); protected Thread[] getcounters() return counters; protected ThreadGroup getgroup() return countergroup; private final int numcounters = 3; private final Thread[] counters = new Thread[numCounters]; private final ThreadGroup countergroup = new ThreadGroup("counters"); Säikeitä ryhmitellään pääsääntöisesti turvallisuussyistä, sillä luokka SecurityManager hoitaa turvallisuusnäkökohdat säieryhmien pohjalta. Ryhmäpohjainen turvallisuus perustuu siihen, että yhdessä ryhmässä oleva säie ei saa muuttaa toisessa ryhmässä olevaa säiettä muuta kuin siinä tapauksessa, että muutettava säie on muuttavan säikeen aliryhmässä. Esimerkiksi ladattavaan applettiin kuuluvat säikeet sijoitetaan aina omaan ryhmäänsä, jotta ne eivät voisi häiritä muita JVM:n alaisuudessa samanaikaisesti toimivia säikeitä. PiirteencheckAccess (löytyy luokista Thread ja ThreadGroup) avulla voidaan tarkistaa, onko nykyisellä säikeellä lupa mennä muuttamaan toista säiettä tai ryhmää. Säieryhmien hallintaan on myös setmaxpriority, joka määrittää annetun ryhmän uusille säikeille suurimman mahdollisen prioriteetin, jota ne voivat käyttää. Tällä tavoin voidaan estää se, että vihamieliset säikeet ottaisivat hallintaansa koko systeemin antamatta kontrollia muille. Demonisäikeiden (daemon threads) on tarkoitus tarjota taustalla yleispalveluja (laiteajurit, postipalvelin yms.), mutta normaalisti niiden ei katsota muodostavan oleellista osaa itse sovellusalueesta. Demonisäikeet luodaan normaaliin tapaan, mutta niille annetaan erityisstatus luokan Thread rutiinilla setdaemon(boolean on), jonka argumentiksi annetaan true. Demonisäie voidaan myöhemmin palauttaa tavalliseksi kuolevaiseksi antamalla samalle rutiinille argumentti false. Säie on julistettava demoniksi setdaemon-kutsulla ennen säikeen käynnistämistä (start). Demonin luomat alisäikeet ovat automaattisesti myös demoneja. Demonit poikkeavat normaaleista ns. user-säikeistä myös ohjelman suorituksen loppuessa: main-säie odottaa tällöin aina normaalisäikeiden loppumista, mutta demonisäikeiden suoritus katkaistaan väkisin.

36 28 LUKU 3. SÄIKEET (BY TIMO RAITA)

37 Luku 4 Johdatus hajautettuihin sovelluksiin Nykyisin tietokoneverkkoja käyttävät ohjelmat perustuvat lähinnä TCP/IP-protokollaperheen (Transmission Control Protocol/Internet Protocol) protokollien käyttöön. Itse asiassa kyseiset protokollat kehitettiin ja vietiin ohjelmointikieliin kirjastojen muodossa jo 70/80-lukujen taitteessa. Vaikka verkossa käytettävät laitteet ovatkin ajan saatossa muuttuneet ja protokollia on tullut runsaasti lisää, niin TCP/IP:n olemus on pysynyt käytännössä muuttumattomana. Muutos on kuitenkin tulossa kun nykyinen IPv4 on tarkoitus lähiaikoina muuttaa IPv6:ksi 1. TCP/IP-pohjainen kommunikaatio perustuu ns. sokettien käyttöön siihen perehdytään luvussa 5. Soketit muodostavat helpon tavan välittää tietoa ohjelmasta toiseen TCP-soketit nähdään Javaohjelmassa kommunikoivien osapuolten kannalta tiedostovirroiksi, joiden kautta voidaan kirjoittaa ja lukea vaikkapa tekstiä tai yleisemmin sarjoittuvia olioita. Luvussa 5 perehdytään myös toiseen IP:n päällä ajettavaan protokollaan ja siihen liittyviin toisenlaisiin soketteihin, nimittäin UDP:hen (User Datagram Protocol). Vaikka verkossa onkin kätevää välittää tietoa sarjoittuvina Java-olioina, niin aina tämä ei ole mahdollista. Saattaa nimittäin olla, että toinen osapuoli ei ole Java-ohjelma. Lisäksi Java-ohjelmien keskenkin sarjoittuvuuden käyttöä haittaa mahdollisuus siitä, että ohjelmat käyttävät eri versiota ko. luokista. Eräs yleinen ratkaisu tällaiseen rakenteisen tiedonvälittämisongelmaan eri verkkosovellusten välillä on XML (extensible Markup Language). XML:ään ja sen tarjoamiin mahdollisuuksiin tässä yhteydessä perehdytään luvussa 9. Java tarjoaa myös muita tapoja muodostaa keskenään verkon välityksellä kommunikoivia ohjelmia. Näistä RMI (Remote Method Invocation) on sellainen, että kommunikoivien ohjelmien tulee olla Java-ohjelmia. Nimensä mukaan RMI mahdollistaa metodien etäkutsumisen eli ohjelma voi soveltaa verkon yli metodin kutsua toisessa verkon koneessa olevaan Java-olioon. RMI on selvästi saanut vaikutteita RPC:stä (Remote Procedure Call; Sun Microsystems) ja CORBAsta (Common Object Request Broker Architecture). RMI:hin perehdytään luvussa 6. CORBAan ei tämän materiaalin puitteissa tutustuta, mutta CORBAn käyttäminen on hyvin samantapaista kuin RMI:kin käyttäminen. Luvussa 10 perehdytään vielä ns. servlettien, eli palvelinsovelmien, yhteydessä käytettävään varsin erilaiseen kommunikaatiotapaan. Servlettejä on nimittäin tarkoitus suorittaa WWW-palvelimessa ja niitä kutsutaan HTTP-protokollaa (HyperText Transfer Protocol) käyttäen. 1 IPv6:n tekninen valmius on jo useilla käyttöjärjestelmillä ja tuki sille on myös mallinnettu ohjelmointikielten kirjastoihin. Itse käyttöönotto on viivästynyt ja viivästynyt, vaikka tekniset edellytykset ovat olleet kunnossa jo kauan. Kysymys on siirtymisen kustannuksista yksittäisille organisaatioille, mutta ennen kaikkea siitä, että IPv4-osoiteavaruuden omistajuus on miljardien dollarien vuosittainen bisnes muutamille Yhdysvaltalaisille yrityksille. 29

38 30 LUKU 4. JOHDATUS HAJAUTETTUIHIN SOVELLUKSIIN 4.1 Asiakas-palvelin sovelluksista yleensä Tietokoneverkot ovat nykyisellään varsin monimutkaisia, mutta onneksi verkkosovellusten käyttämiseksi eikä oikeastaan niiden ohjelmoimiseksikaan tarvitse tietää niiden rakenteen monimutkaisuudesta juuri mitään. Sovellusten kannalta verkko on yksinkertainen abstraktio, joka koostuu osoitettavissa olevista osapuolista verkossa (IP-osoitteet, porttinumerot,... ) ja yhteyden muodostus- ja keskustelutavoista. Pakettien reittäminen verkossa on keskustelevien osapuolten kannalta (lähes) täysin automaattista. Sovellusten tekemisen kannalta tulee tosin ymmärtää, että verkko ei aina ole kaikilta osiltaan ehjä jokin yhteys/laite voi olla hetkellisesti poissa verkosta tai jopa rikki. Lisäksi ongelmatilanteita aiheuttaa se, miten paketteja kuljetetaan verkossa. Kun verkon osapuoli A haluaa lähettää jotakin osapuolelle B, niin se ei tarkoita, että verkossa olisi varattu / varattaisiin 2 tätä varten jokin erityinen kanava, jota pitkin A ja B voisivat keskustella häritsemättä muita verkossa tapahtuvia samanaikaisia keskusteluita. Ethernet-verkossa useat laitteet voivat samanaikaisesti yrittää lähettää jotakin ja jos niin tapahtuu, niin lähetykset törmäävät 3 ja sen seurauksena mitään ei saada sillä kertaa perille vaan lähettävät osapuolet joutuvat yrittämään lähettämistä myöhemmin uudelleen. Toinen huomion arvoinen seikka on, että paketit eivät suinkaan kulje lyhintä reittiä pitkin lähteestä kohteeseen, vaan esim. muutamat Ethernet-verkon laitteet ovat lähes täysin tietämättömiä siitä, missä paketin vastaanottaja tarkalleen ottaen on. Siksi ne toimittavat paketin laajalle alueelle 4 sillä ajatuksella, että vastaanottaja on kyseisellä alueella (tai alueella on sellainen verkon elementti, joka tietää, missä suunnassa vastaanottaja on). Hajautettu sovellus koostuu useista eri koneissa toimivista ohjelmista, jotka keskustelevat toistensa kanssa verkon yli. Itse asiassa ohjelmien ei tarvitse toimia eri koneissa, vaan kaikki voi tapahtua myös koneen sisällä mutta tuolloinkin keskustelu ohjelmien välillä tapahtuu yleensä verkkoprotokollia käyttäen. Kaikkien ohjelmien ei tarvitse keskustella kaikkien kanssa yleensä jokin tai jotkin ohjelmista toimivat palvelimina muiden ollessa asiakkaita ja muodostettavissa verkkoyhteyksissä toisena osapuolena on aina palvelin. Ohjelmien välinen kommunikointi tarkoittaa sitä, että sovellukset keskustelevat keskenään käyttäen jotakin sovellustason kieltä siis vaihtaen tietynlaisia viestejä keskenään (tietynlaisen järjestyksen mukaan). Kyseiset viestit kuljetetaan lähettäjältä vastaanottajalle käyttäen verkkoprotokollia (ja ohjelmien ollessa eri koneissa, käyttäen tietokoneverkkoa). Kommunikoivien ohjelmien ei tarvitse olla tehty samalla ohjelmointikielellä. Esimerkiksi soketit (luku 5) on aluksi esitelty ohjelmointikieliin C-kielen kirjastojen kautta, mutta Java-kieliset soketteja käyttävät ohjelmat voivat hyvin keskustella C-kielisten ohjelmien kanssa. Kommunikoinnin suhteen ohjelmat voidaan pyrkiä jakamaan kahteen kategoriaan: asiakkaisiin ja palvelimiin. Jako ei kuitenkaan ole täydellinen, sillä ohjelma voi toimia molemmissa rooleissa samanaikaisesti (mahdollisesti eri ohjelmajoukkojen suuntaan eri roolissa). Jako perustuu rooliajatteluun ja siihen, että palvelimeksi mielletään sellainen, jolla on jotain resurssia (tietoa, välityskykyä, tms) jaettavanaan, ja asiakas puolestaan on ohjelma, joka haluaa jotain resurssia. Usein palvelin on sovellus, joka kuuntelee yhteydenottopyyntöjä. TCP:n ja RMI:n tapauksessa tämä näkyy selkeästi ohjelmasta UDP:n tapauksessa tunnistaminen on vaikeampaa. Verkkokeskustelun aloittava osapuoli on lähes aina asiakkaan roolissa. 2 ATM-verkoissa toimitaan itse asissa tähän tapaan, mutta Ethernet-verkoissa törmäykset ovat mahdollisia. 3 Törmääminen edellyttää, että lähetysten lähteiden ja kohteiden välillä on jokin yhteinen alue, jota ne kumpikin käyttävät. 4 Tämä tarkoittaa, että lähettäjä-vastaanottaja pariin liittyy tietynlainen (verkosta riippuva) törmäysalue, jossa paketti voi Ethernet-verkossa törmätä toisen kenties aivan eri kohteeseen menevän paketin kanssa.

39 4.1. ASIAKAS-PALVELIN SOVELLUKSISTA YLEENSÄ 31 Verkkosovellukset ja monisäieohjelmat voi tietyssä mielessä rinnastaa. Kummatkin ovat tietyssä mielessä itsenäisiä samanaikaisesti suoritettavista osista koostuvia kokonaisuuksia. Säikeiden tapauksessa keskinäisellä vuorovaikutuksella usein pyritään ohjaamaan toisten säikeiden suoritusta. Toisaalta verkkosovellukset vaihtavat yleensä keskenään passiivista tietoa, jolla ei suoranaisesti pyritä ohjaamaan toisen osapuolen toimintaa epäsuorasti välitettävä tieto toki vaikuttaa ohjelman suoritukseen. Verkkosovellusten ja monisäieohjelmien osalta kannattaa kuitenkin huomata, että periaatteessa kaikki monisäieohjelmien tekemiseen liittyvät ongelmat (luvut 2 ja 3) ovat myös verkkosovellusten ongelmia. Esimerkkejä verkkosovelluksista on helppo löytää: Erilaiset verkkopelit ovat sovelluksia, jossa palvelin ylläpitää pelipöytää ja asiakkaat vain tekevät siirtoja sekä esittävät pelitilannetta omalta kannaltaan. Huomaa kuitenkin, että tällaisissa sovelluksissa palvelimen tehtävä on varsinaisesti pitää kirjaa pelitilanteesta. Informaatiopalvelut perustuvat usein ratkaisuun, jossa palvelinsovellus ylläpitää tietovarastoa yleensä jonkin tietokannan muodossa ja asiakkaat tekevät tiedon hakuja sekä mahdollisesti päivityksiä. Asiakkaiden rooli on erilaisten toimenpiteiden ilmaisemisen mahdollistamisessa sekä luonnollisesti tulosten esittämisessä. Edellisen yksinkertaistettuja muotoja ovat esimerkiksi erilaiset tiedoituspalvelut: kellon ajan kertominen, noppien arpomispalvelu, WWW-palvelu,.... Tähän kategoriaan voidaan sovittaa myös esimerkiksi sähköpostin luku- ja välityspalvelut tai monimutkaisemmat WWW:n kautta tapahtuvat palvelut, kuten esimerkiksi verkkokauppa. Kun verkkosovelluksia tehdään, pitää ratkaista mm. seuraavia ongelmia. Miten konkreettisesti välittää tietoa? Miten asiakas tietää, missä palvelin on? Miten palvelin osaa vastata asiakkaalle? Miten siis yhteyden muodostaminen & lopetus tapahtuu? Entä jos palvelimella on useita asiakkaita samanaikaisesti? Entä jos asiakas käyttää useita palvelimia? Mitä ongelmia samanaikaisuudesta on? Palvelin: miten palvella kaikkia tasapuolisesti? Miten hallita hajautetun sovelluksen siirtymistä tilasta toiseen? Mitä tietoa asiakkaan ja palvelimen välillä tulisi missäkin vaiheessa välittää? Moniin edellisistä kysymyksistä antaa vastauksen käytettävä verkkoprotokolla, mutta samanaikaisuuteen ja kokonaisuuden hallintaan liittyvät ongelmat ovat lähinnä suunnittelulla hallittavia kysymyksiä.

40 32 LUKU 4. JOHDATUS HAJAUTETTUIHIN SOVELLUKSIIN 4.2 Verkosta ja verkkoprotokollista Kommunikointi tietoverkoissa on hyvin kurinalaista, se tapahtuu käyttäen jotakin protokollaa. Protokolla tarkoittaa muotokieltä, eli lähetettävien viestien muoto on tarkkaan määritelty, mutta tyypillisesti protokolla on enemmän kuin pelkkä viestien muoto protokolla usein etenee tietyn vuoropuhelukaavan mukaan. Tuolloin voidaan ajatella, että protokolla on keskustelevien osapuolten (siis sovellusten) kannalta tietyssä tilassa. Protokollaan tyypillisesti liittyy myös mahdollisuus nimetä keskustelevat osapuolet. Nykyisin suuri osa tietokoneverkoista on kytketty toisiinsa ja yhdessä ne muodostavat maailmanlaajuisen ns. Internet-verkon. Internet-verkossa koneilla on ns. Internet-nimi, esimerkiksi alhena.utu.fi. Internet-nimi on symbolinen nimi. Varsinainen liikennöinti verkossa ei perustu siihen vaan ns. IP-osoitteeseen, esimerkiksi IP-osoitteiden muoto on tarkkaan määritelty ja IPv4 5 :n yhteydessä osoite koostuu neljästä kokonaisluvusta väliltä , eli osoitteista Yhtä IP-osoitetta kohti on yksi Internet-nimi 6. Tyypillisesti koneella on yksi verkkokortti ja sitä kautta koneella on yleensä yksi IP-osoite, mutta koneella voi olla useita verkkokortteja ja sitä kautta se voi olla kytkettynä useaan verkkosegmenttiin 7. Kuhunkin koneeseen liittyy yksi yhteinen Internet-osoite, localhost. Ohjelman kannalta nimi localhost tarkoittaa sitä konetta, jossa ohjelmaa suoritetaan. Vaikka localhost onkin verkkoosoite, niin itse asiassa sen käyttäminen ei vaadi, että kone olisi kytketty verkkoon. Huomaa, että osoitteen localhost avulla sovellukset voivat koneen sisällä keskustella IP-protokollalla ilman todellista verkkoyhteyttä. Internet-verkon käyttäjän sen enempää kuin ohjelmoijankaan ei pääsääntöisesti tarvitse tuntea koneiden IP-osoitteita. Tämä johtuu siitä, että Internet-verkossa on (maailmanlaajuinen, hierarkinen) nimipalvelu 8, jolta sovellukset voivat kysyä symbolista nimeä vastaavaa IP-osoitetta (tai kääntäen). Vaikka nimipalvelu itsessään on varsin kompleksinen järjestelmä, verkkosovellusten tekijän ei tarvitse tietää siitä juuri muuta kuin, että se on potentiaalinen ongelmien lähde osa verkon laitteista on nimittäin aina rikki ja siten kaiken nimipalvelun takana olevan tiedon ei aina tarvitse olla saavutettavissa. Internet-verkkoa käyttävien Java-ohjelmien yhteydessä Internet-nimiä vastaavien IPosoitteiden selvittäminen tapahtuu tyypillisesti automaattisesti. Vaikka liikennöinti Internet-verkossa perustuukin IP-protokollan käyttöön, niin yleensä sovellukset välittävät tietoa käyttäen IP-protokollan päällä suoritettavaa TCP- tai UDP-protokollaa. Verkkoliikenteen ja verkon ylipäänsä voidaan nähdä olevan monikerroksista. ISO (International Organization for Standards) on määritellyt ns. OSI-mallin (Open System Interconnection) verkon rakenteen ja toiminnan selittämiseksi. OSI-malli on 80-luvun alkupuolelta ja siinä on 7 kerrosta. Nykyisin verkko yleensä kuvataan 4-kerroksiseksi 9 heijastaen suoraan verkossa välitettävien pakettien rakennetta. Kuvassa 4.1 on esitettynä tyypillisesti verkossa liikutettavien pakettien rakenne. Kuten kuvasta näkyy, pakettien rakenne on kerroksittainen: esimerkiksi fyysisen verkkokerroksen Ethernet-paketti pitää sisällään IP-paketin, joka puolestaan pitää sisällään TCP-paketin, joka puolestaan koostuu sovellukseen liittyvästä tiedosta. Internet-verkko koostuu paikallisista pienistä verkoista (LAN, Local Area Network), jotka on yhdistetty toisiinsa erilaisilla reititinlaitteilla (toistin, keskitin, silta, kytkin, reititin,... ). Verkon laitteita ja rakennetta on havainnollistettu kuvassa 4.2. Yksinkertaisimmillaan paikallinen verkko koostuu esi- 5 Internet Protocol, version 4. 6 Tosin sillä nimellä voi olla useita aliaksia. 7 Itse asiassa yhteen verkkokortiinkin voi liittyä monta IP-osoitetta. 8 DNS, Domain Name Service. 9 Joskus käytetään vain kolmea kerrosta yhdistäen 4-kerroksisen mallin kaksi keskimmäistä kerrosta.

41 4.2. VERKOSTA JA VERKKOPROTOKOLLISTA 33 välitettävä data sovellusotsikko TCP-otsikko IP-otsikko LAN-otsikko Kuva 4.1: Verkon kerrokset. merkiksi Ethernet-verkon segmentistä. Paikallisessa verkossa siirretään paketteja käyttäen sen kieltä esimerkiksi Ethernet-verkossa siirretään Ethernet-paketteja. Tätä verkon alinta kerrosta kutsutaan fyysiseksi kerrokseksi tai siirtoyhteyskerrokseksi. Yleensä tämä kerros perustuu Ethernet- tai ATM-verkkoon. Verkossa välitettävien pakettien kannalta tämä tarkoittaa, että paketit ovat aina LANpaketteja, jotka pitävät sisällään jotain tietoa. Niihin kuitenkin liittyy kyseisen verkon mukaisia tietoja esimerkiksi Ethernet-verkkokorteilla on osoite, joka on esimerkiksi 0:0:3b:80:37:80 se koostuu siis kuudesta 2-numeroisesta 16-kantaisesta luvusta. ATM-verkoissa koneilla on myös osoitteet, mutta ne ovat toisenlaisia. Siirtoyhteyskerroksen päällä toimii verkkokerros, jota IP-verkkojen yhteydessä usein kutsutaan IP-kerrokseksi. IP-pakettien otsikoissa on fyysisen kerroksen pakettien tapaan tietoja, joita tarvitaan pakettien reitittämiseksi verkossa läheteestä kohteeseen. Suuri osa verkon laitteista siirtää (kopioi) paketteja yhdestä verkon osasta toiseen käyttäen fyysisen kerroksen otsikkotietoja, mutta esimerkiksi reitittimet käyttävät jo IP-kerroksen tietoja reitityksen toteuttamisessa. Ylimmällä sovelluskerroksella välitetään sovelluksiin liittyvää dataa niiden määrittelemässä muodossa tosin, jos sovellusten välillä halutaan siirtää paljon dataa, pitää se yleensä jakaa useisiin IP-paketteihin (ja samalla useisiin TCP/UDP-paketteihin). Sovelluskerroksen sovelluksia ovat mm. SSH, TELNET, FTP, WWW-selaimet ja palvelimet sekä sähköpostia käsittelevät ohjelmat.

42 34 LUKU 4. JOHDATUS HAJAUTETTUIHIN SOVELLUKSIIN Silta Ethernet kytkin Ethernet Switch Silta Telco/LAN Router ATM kytkin Reititin Kuva 4.2: Verkon komponentteja.

43 Luku 5 Soketit ja niiden käyttö Tässä luvussa perehdytään jo 70/80-lukujen taitteessa muotoutuneeseen tapaan välittää tietoa eri koneissa suoritettavien sovellusten välillä tietokoneverkkoa käyttäen. Soketti (engl. socket) on sovellusten välinen kytkös (kone1, portti1) (kone2, portti2) siten, että kumpikin kone tietää kytköksen olemassaolon (UDP-sokettien tapauksessa kytkös on tosin varsin löyhä, lähes olematon). Seuraavaksi luvussa 5.1 kerrotaan sokettien perusteita ja muodostetaan näkemys sokettien käytöstä koneen ja sovelluksen kannalta. Luvuissa 5.2 ja 5.3 perehdytään UDP- ja TCP-soketteihin ja niihin liittyviin luokkiin esimerkkien avulla. Luvussa 5.4 perehdytään hieman erikoisempiin sokettien käyttötilanteisiin: ryhmälähetykseen ja sokettien käyttöön applettien yhteydessä. 5.1 Perustietoa soketeista Soketti on IP:n päällä toimiva kanava, jonka kautta sovellus voi sekä lähettää että vastaanottaa paketteja 1. IP-osoitteen lisäksi sokettiin liittyy aina porttinumero, joka on kokonaisluku Periaatteessa ohjelmoija voi vapaasti valita luomalleen sokettioliolle sen käyttämän porttinumeron, mutta käytännössä omien sokettiyhteyksien tulisi käyttää portinumeroita , sillä porteille on määritelty stardardimerkitys 2 IANAn (Internet Assigned Numbers Authority) toimesta. Stardardimerkitys tarkoittaa sitä, että kyseinen portti on varattu tietyn Internet-palvelun käyttöön. Yleistä sokettien käyttöön liittyvää tilannetta yksittäisen koneen kannalta on havainnollistettu kuvassa 5.1. Koneen sisällä tavallisesti toimii useita sovelluksia samanaikaisesti ja kukin niistä voi käsitellä samanaikaisesti yhtä tai useampaa sokettia (ja sitä kautta porttia). Koneen sisällä toimiviin sovelluksiin liittyvien sokettien tulee käyttää eri porttinumeroita. Käytännössä sokettiolion 3 luominen aiheuttaa sen rekisteröimisen koneen sisällä verkkokorttin toimintaan liittyvälle ohjelmalle. Tuon rekisteröitymisen seurauksena verkkokortin nappaamat kyseiselle koneelle ja tietyyn porttiin osoitetut paketit osataan koneen sisällä toimittaa oikealle sovellukselle. Vastaavasti lähetystoiminnan yhteydessä sokettiyhteyden kautta lähetettyihin paketteihin osataan liittää lähettäjän soketin porttinumero (IP-numeron lisäksi). Sokettiyhteyden kautta lähetetään tietoa lähettävästä soketista vastaanottavaan sokettiin. Tietoa voidaan sokettien avulla välittää kolmella eri tavalla. 1 Soketit voivat toimia myös koneen sisällä ilman IP-verkkoa, mutta sellaista tilannetta ei seuraavassa tarkastella. Esimerkiksi, soketteja on käytetty Unix:n puitteissa jopa levyoperaatioiden toteutuksessa. 2 Esim. SSH käyttää porttia numero 22. Ks. Joillakin porttinumeroilla on myös standardinomainen merkitys. 3 Oliokielissä sokettiolion mutta esim. C-kielen tapauksessa socket-tyyppisen struct:n. 35

44 36 LUKU 5. SOKETIT JA NIIDEN KÄYTTÖ Tietokone Ohjelma Ohjelma Ohjelma Portti Portti Portti Verkko Kuva 5.1: Yleiskuva sokettien käytöstä yksittäisen koneen kannalta. Yhteydettömästi UDP-sokettien avulla. Yhteydettömässä lähetyksessäkin on soketti kummassakin päässä, mutta ideana on, että yhteydenotot ovat kestoltaan aina vain yhden paketin mittaisia. UDP-soketteihin ja niiden toteuttamiseen liittyviin luokkiin DatagramPacket ja DatagramSocket perehdytään luvussa 5.2. Yhteydellisesti TCP-sokettien avulla. Yhteydellisyys tarkoittaa, että ennen varsinaista datan lähettämistä sokettiyhteyden ylitse osapuolet neuvottelevat välilleen sokettiyhteyden, joka on pisteestä pisteeseen -tyyppinen (point-to-point). Osapuolet sitoutuvat TCP-sokettien kohdalla yhteyteen aivan eri tavalla kuin UDP-sokettien kohdalla. TCP-sokettiyhteys muodostaa tosiasiallisesti tiedostovirran molempiin suuntiin sokettiyhteyden yli. UDP:n tapauksessa sokettiyhteyden yli lähetetään yksittäisiä paketteja, mutta TCP:n tapauksessa vain kirjoitetaan (ja vastaavasti luetaan) sokettiin liittyvään tiedostovirtaan 4. Luvussa 5.3 perehdytään TCP-sokettien olemukseen ja käyttöön esimerkkien avulla. Samalla tehdään selkoa Javan luokista Socket ja ServerSocket sekä tutkitaan soketteihin liittyvien tiedostovirtojen käyttöä (luokat InputStream, DataInputStream, ObjectInputStream,... ). Edellä mainitut tavat käsitellä paketteja ovat ns. täsmälähetyksiä (unicast-tyyppisiä), eli niissä lähetetään tietoa yhdestä paikasta yhdelle vastaanottajalle. Verkossa voidaan lähettää tieto samanaikaisesti myös usealle vastaanottajalle. Ns. ryhmälähetyksissä (engl. multicast) lähetetty paketti toimitetaan usealle vastaanottajalle ryhmään kuuluville vastaanottajille. Ryhmälähetykset edellyttävät vastaanottajilta ryhmään rekisteröitymistä. Toinen yhdestä moneen - lähetysten muoto ovat yleislähetykset (engl. broadcast), joissa vastaanottajat määritetään niiden IP-osoitteiden perusteella (ei tarkastella tässä). Ryhmälähetyksiä käsitellään kohdassa Luokka InetAddress Soketteihin liittyvät luokat ovat Javan yhteydessä paketissa java.net. Kuten on tullut ilmi, sokettien käyttämisen yhteydessä tarvitaan porttinumeroita ja IP-osoitteita. IP-osoitteita varten luokkakirjastossa on luokka java.net.inetaddress, jonka metodeita on lueteltu taulukossa Viime kädessä TCP-sokettiyhteyden käyttäminen tosin tarkoittaa pakettien lähettämistä, mutta TCP:n tapauksessa käyttäjän ei tarvitse itse tehdä pakettien muodostamista.

45 5.2. YHTEYDETÖN UDP 37 Vaikka luokan mukaisia olioita onkin olemassa, niin luokalla ei ole lainkaan julkisia konstruktoreita. Ideana on, että oliot luodaan luokkametodeilla getbyname ja getlocalhost. Näistä edellinen muodostaa olion käyttäen joko internet-nimeä tai IP-osoitetta. static InetAddress getbyname(string host) Muodostaa annettavan internet-nimen tai IPosoitteen perusteella IP-osoitteen. Esim. alhena.utu.fi Tuottaa poikkeuksen UnknownHostException, jos nimipalvelusta ei löydy internet-nimeä vastaavaa IP-osoitetta. static InetAddress getlocalhost() Koneen, jossa ohjelmaa ajetaan, IP-osoite. Tuottaa poikkeuksen UnknownHostException, jos koneen IP-osoitetta ei ole määritetty. String gethostaddress() Palauttaa osoitteen numeromuodossa (esim ). String gethostname() Palauttaa IP-osoitetta vastaavan internet-osoitteen. boolean ismulticastaddress() Onko multicast-osoite? Taulukko 5.1: Joitakin luokan InetAddress metodeja. Kun taulukon 5.1 metodeissa tarvitaan yhteyttä IP-osoitteen ja internet-nimen välille, niin kyseinen tieto yritetään hakea nimipalvelusta (DNS). Tätä kautta voi tulla ongelmia, jos tietoja ei löydy nimipalvelusta. Toinen mahdollinen ongelma on turvallisuuspoikkeuksen syntyminen, jos yhteydenottoa nimipalveluun ei ole (turvallisuusmanagerin toimesta) sallittu. 5.2 Yhteydetön UDP UDP-yhteyden ideana on olla kevyt ja nopea yhteys. UDP-soketit muodostavat yhteydettömän kanavan. Tämä tarkoittaa käytännössä, että pakettien järjestys voi matkalla vaihtua, tai jotkin paketit voivat kokonaan kadota matkalla. Vastaanotettuja paketteja ei kuitata mitenkään, ja ylipäänsä pakettien lähettämisessä ja vastaanottamisessa ei ole mitään automaattista kontrollia, jonka avulla virheelliset 5 tai matkalla kadonneet paketit voitaisiin pyytää lähetettäväksi uudelleen. UDP on siis pakettien perilletoimittamisen kannalta varsin epäluotettava yhteys mutta sillä on kuitenkin monia järkeviä käyttötarkoituksia. UDP-yhteyttä käytetään Java-ohjelmissa siten, että lähettäjä ja vastaanottaja luovat kummatkin itselleen DatagramSocket-tyyppisen olion. Sen jälkeen lähettäjä luo yhden tai useamman paketin, jotka se sokettiyhteyden kautta lähettää vastaanottajalle. Paketit ovat luokan DatagramPacket olioita ja vastaanottajankin pitää luoda itselleen sellaisia vastaanottamisen yhteydessä verkosta tuleva tieto kopioidaan vastaanottajan sokettiyhteydelle tarjoamaan vastaanottavaan DatagramPacket-olioon. Javan DatagramPacket-luokan metodeita ja konstruktoreita on esitelty taulukossa 5.2. Verkossa liikkuvaan UDP-pakettiin liittyy lähettäjän ja vastaanottajan porttinumero (ja UDP-paketin sisäänsä sulkevaan IP-pakettiin liittyy vastaavasti lähettäjän ja vastaanottajan IP-osoite). Tässä mielessä saattaa tuntua hieman oudolta, että DatagramPacket-olion tietosisältönä on vain (lähetettävä) data tavuina, yksi IP-osoite ja yksi porttinumero. Selitys yhdelle IP-osoitteelle ja porttinumerolle on, että lähettämisen yhteydessä ne tiedot tarkoittavan kohteen tietoja ja vastaanotettujen pakettien kohdalla kyseiset tiedot ovat puolestaan lähettäneen tahon tietoja! Taulukossa 5.3 on esitetty luokka DatagramSocket, joka on soketti datagrammien lähettämiseksi ja vastaanottamiseksi. Oletusarvoisesti UDP-soketin kautta voi lähettää paketteja minne tahansa ja vastaavasti ottaa vastaan mistä tahansa koneesta, mutta connect -metodilla on mahdollista rajoittaa soketin toimintaa tässä mielessä. UDP-soketin luonnin yhteydessä se kiinnitetään johonkin paikallisen 5 UDP-paketeissa on tarkistussumma, joten korruptoituneet paketit sentään voidaan havaita.

46 38 LUKU 5. SOKETIT JA NIIDEN KÄYTTÖ DatagramPacket(byte[] b, int l, InetAddress a, int p) Luo paketin, jonka sisältö on l tavua b:stä, kohteena koneen a portti p. DatagramPacket(byte[] b, int l) Luo paketin vastaanottamista varten. InetAddress getaddress() Palauttaa (vastaanotetun / luodun) paketin (lähettäjän / vastaanottajan) osoitteen. int getport() Palauttaa pakettiin liittyvän porttinumeron. byte[] getdata() Paketin data. int getlength() Paketin datan pituus. Lisäksi vastaavat set-metodit (data, IP-osoite, portti). Taulukko 5.2: Joitakin luokan DatagramPacket konstruktoreita ja metodeja. koneen porttiin. Peruskäyttö perustuu tämän jälkeen kahteen metodiin: send ja receive. Niiden avulla lähetetään ja vastaavasti vastaanotetaan yksi UDP-paketti (DatagramPacket). DatagramSocket(int port) Luo soketin paikallisen koneen porttiin port. Tuottaa poikkeuksen SocketException, jos sokettia ei voitu luoda ko. porttiin. Poikkeus SecurityException puolestaan tarkoittaa, että turvallisuusmanageri ei salli operaatiota. DatagramSocket() Luo soketin johonkin vapaaseen paikallisen koneen porttiin (vapaa porttinumero allokoidaan kysymällä sitä koneen sisäisesti rekisteröintipalvelulta ). DatagramSocket(int port, InetAddress laddr) Kuten edellä, mutta käyttäen paikallisen koneen osoitetta laddr (koneella voi olla useita osoitteita). int getlocalport() Palauttaa soketin portin. InetAddress getlocaladdress() Palauttaa sokettiin liittyvän paikallisen koneen osoitteen. void send(datagrampacket p) Lähettää soketin kautta paketin p paketissa kerrottuun kohteeseen. void receive(datagrampacket p) Vastaanottaa soketin kautta paketin p:hen (jos joku siis sellaisen lähettää vastaanottamisen käynnistämisen jälkeen). Huomaa osoitteiden käyttö paketissa ja soketissa. Vastaanotetulla paketilla on lähettävän soketin tiedot! Poikkeus IOException syntyy, jos paketin vastaanottamisessa tulee jokin virhetilanne. void setsotimeout(int timeout) Asettaa odotusajan paketin vastaanottamiselle. Jos aikaraja = 0, niin odotusaika on rajoittamaton. Metodi nostaa poikkeuksen SocketException, jos aikarajan asettaminen ei onnistu. Aikarajan merkitys liittyy receive -metodin toimintaan. Jos aikaraja ylittyy: receive tuottaa poikkeuksen InterruptedIOException (tai siis sitä suorittavaan säikeeseen kohdistuu kyseinen poikkeus) Esimerkki UDP-paketin välityksestä Seuraavaksi tarkastellaan yksinkertaista esimerkkiä, jossa asiakassovellus (luokka UDPLahettaja esimerkissä 5.1) mahdollistaa komentoriviparametrien avulla paketin lähettämisen ilmaistuun kohteeseen. Vastaanottavana puolena toimii GUI:lla varustettu ohjelma (luokka UDPVastaanottaja esimerkissä 5.2), joka kuuntelee tietyssä UDP-portissa sille osoitettuja lähetyksiä ja vastaanotettuaan viestin, tulostaa sen GUI:n ylimmässä tekstikomponentissa. Kuva vastaanottajan käyttöliittymästä on kuvassa 5.2.

47 5.2. YHTEYDETÖN UDP 39 int getsotimeout() Palauttaa odotusajan. void close() Sulkee soketin. void connect(inetaddress a, int p) Kytkee soketin operoimaan vain kohteen (a, p) kanssa (tarkistukset puutteellisia). Oletusarvoisesti UDP-sokettiyhteys voi operoida kaikkien kohteiden kanssa. void disconnect() Purkaa edellisen kytkennän. InetAddress getinetaddress() Kohdekoneen osoite, johon soketti on kytketty. Arvo null tarkoittaa, että sokettia ei ole kytketty johonkin tiettyyn kohteeseen. int getport() Portti, johon soketti on kytketty (arvo -1 = ei kytketty). Taulukko 5.3: Joitakin luokan DatagramSocket konstruktoreita ja metodeja. Esimerkki 5.1 Viestin lähettämien UDP-pakettina. import java.io. ; import java.net. ; public class UDPLahettaja public static void main(string[] args) throws Exception // Parametrit: kohdekone, portti, viesti if (args.length < 3) System.out.println("Parametrit: kone, portti, viesti"); System.exit(0); InetAddress kohdekone = InetAddress.getByName(args[0]); int kohdeportti = Integer.parseInt(args[1]); DatagramSocket soketti = new DatagramSocket(); byte[] sisältö = args[2].getbytes(); DatagramPacket paketti = new DatagramPacket(sisältö, sisältö.length, kohdekone, kohdeportti); soketti.send(paketti); // main // UDPLahettaja Kuten esimerkistä 5.1 voi havaita, lähettäjän toiminta on hyvin suoraviivaista. Luodan vain UDPsokettiolio, UDP-pakettiolio ja lähetetään pakettiolion sisältö kohteelle sokettiolion send :llä.

48 40 LUKU 5. SOKETIT JA NIIDEN KÄYTTÖ Esimerkki 5.2 GUI-asiakas, joka ottaa vastaan UDP-pohjaisia viestejä. import java.net. ; import javax.swing. ; import java.awt. ; import java.awt.event. ; import java.io.interruptedioexception; public class UDPVastaanottaja public static final int PORTTI = 2000; public static void main(string[] args) throws Exception JFrame f = new JFrame("UDPVastaanottaja"); Container c = f.getcontentpane(); Font tr18 = new Font("Times-Roman", Font.BOLD, 18); JTextField kohde = new JTextField(60); final JTextField tf = new JTextField(30); kohde.setfont(tr18); tf.setfont(tr18); final PorttiKuuntelija kuuntelija = new PorttiKuuntelija(PORTTI,kohde); JButton b = new JButton("Aseta portti"); b.addactionlistener(new ActionListener() public void actionperformed(actionevent e) int p = PORTTI; try p = Integer.parseInt(tf.getText().trim()); catch (Exception e1) kuuntelija.setnewport(p); ); c.add(kohde, BorderLayout.NORTH); c.add(tf, BorderLayout.SOUTH); c.add(b); f.pack(); f.setvisible(true); f.addwindowlistener(new IkkunanSulkija()); kuuntelija.start(); // main static class PorttiKuuntelija extends Thread private int portti; private JTextField kohde; private DatagramSocket soketti; private boolean validi; public void setnewport(int p) validi = (p == portti); portti = p; public PorttiKuuntelija(int p, JTextField t) throws Exception portti = p; kohde = t; validi = true; soketti = new DatagramSocket(p); soketti.setsotimeout(5000); // PorttiKuuntelija public void run() try byte[] alue = new byte[256]; DatagramPacket saatu = new DatagramPacket(alue, alue.length); while (true) if (!validi) soketti.close(); soketti = new DatagramSocket(portti); soketti.setsotimeout(5000); try soketti.receive(saatu); catch (InterruptedIOException e) continue; String viesti = new String(saatu.getData(), 0, saatu.getlength()); String uusi = saatu.getaddress().gethostname() + ":" + saatu.getport() + "says " + viesti; kohde.settext(uusi); // while catch (Exception e) throw new Error(e.toString()); // run // class PorttiKuuntelija // class UDPVastaanottaja

49 5.3. YHTEYDELLINEN TCP 41 Luokassa UDPVastaanottaja luodaan käyttöliittymä normaaliin tapaan käyttöliittymään on liitetty yksi tapahtumankäsittelijä, jonka avulla alimpaan kenttään kirjoitettu numero voidaan asettaa keskellä olevaa nappulaa painamalla uudeksi porttinumeroksi (jota jatkossa tarkkaillaan). Huomaa lisäksi, että itse portin kuuntelu ei tapahdu GUI:n luoneen säikeen toimesta, vaan sitä varten käynnistetään main -metodissa erillinen säie (lopussa oleva kuuntelija.start();). Huomaa, miten PorttiKuuntelija luo UDP-sokettiyhteyden, johon liittyy viiden sekunnin aikaraja. Tämän aikarajan avulla käyttöliittymän tapahtumankäsittelijän mahdollisesti päivittämä tieto uudesta kuunneltavasta porttinumerosta saavuttaa PorttiKuuntelija :n enintään tuon viiden sekunnin kuluttua. Huomaa myös, miten run :n while-silmukan alussa mahdollisesti luodaan uusi sokettiyhteys, jos GUI:n toimesta on päätetty alkaa kuuntelemaan eri porttia. Kuva 5.2: UDP-pakettien vastaanottaminen GUI-sovelluksella. Kuvan 5.2 tilanteessa on otettu vastaan paketti koneelta bg.cs.utu.fi lähettäjän portista 3109 (vastaanottajan portti on ollut 2000). UDP-paketin sisältönä on ollut Heips... mitä kuuluu?. Alimpaan tekstikenttään käyttäjä on kirjoittanut 1000 joka päivittyy uudeksi kuunneltavaksi porttinumeroksi painamalla keskellä olevaa nappulaa. 5.3 Yhteydellinen TCP Yhteydellisen TCP-sokettiyhden ideana on, että pakettien järjestys ja perille pääsy taataan sokettiyhteyden toimesta ilman, että soketteja käyttävien sovellusten tarvitsee tehdä mitään. TCP-soketit muodostavat 2-suuntaisen tietovirran kahden (yleensä eri koneissa olevan) sovelluksen välille. Tässä kohtaa Javan tiedostovirrat ovat parhaimmillaan: sovelluksen kannalta tällaisia soketteihin liittyviä tietovirtoja käytetään kuten mitä tahansa Javan tiedostovirtoja. Sokettiyhteyden luominen kahden sovelluksen välille tapahtuu TCP:n tapauksessa hieman eri tavalla kuin UDP:n kohdalla. Asiakassovellus luo itselleen Socket-olion (ks. taulukko 5.4), ja luonnin yhteydessä ilmaistaan, mihin kohteeseen yhteys muodostetaan. Palvelinsovellus on vastaavasti jo ajallisesti aiemmin luonut ServerSocket-tyyppisen olion (ks. taulukko 5.5), joka kautta palvelin kuuntelee yhteydenottopyyntöjä konkreettisesti palvelinsovellus kuuntelee ServerSocketyhteyden accept -metodia kutsumalla. Asiakkaan tekemä Socket-olion luonti on samalla yhteydenottopyyntö, jonka perusteella palvelinsovellus muodostaa itselleen Socket-olion accept -metodin kutsun tuloksena. Palvelimen näin muodostama Socket-olio on kiinnittynyt asiakkaan vastaavaan sokettiolioon. Luonnin jälkeen käyttäminen on suoraviivaista. Socket-tyyppisistä olioista otetaan esiin niihin liittyvät sisään- ja ulosmenevät tiedostovirrat, ja käytetään kyseisiä tiedostovirtoja keskustelemiseen. Huomaa, että TCP-sokettien käyttämisen yhteydessä ei itse eksplisiittisesti tarvitse luoda lähetettäviä paketteja, kirjoitetaan vain tiedostovirtaan!

HOJ Säikeet (Java) Ville Leppänen. HOJ, c Ville Leppänen, IT, Turun yliopisto, 2012 p.1/55

HOJ Säikeet (Java) Ville Leppänen. HOJ, c Ville Leppänen, IT, Turun yliopisto, 2012 p.1/55 HOJ Säikeet (Java) Ville Leppänen HOJ, c Ville Leppänen, IT, Turun yliopisto, 2012 p.1/55 Missä mennään... 1. Johdanto (1h) 2. Säikeet (2h) 3. Samanaikaisuudesta (2h) 4. Hajautetuista sovelluksista (1h)

Lisätiedot

Hajautettujen sovellusten muodostamistekniikat, TKO_2014 Johdatus kurssiin

Hajautettujen sovellusten muodostamistekniikat, TKO_2014 Johdatus kurssiin Hajautettujen sovellusten muodostamistekniikat, TKO_2014 Johdatus kurssiin Ville Leppänen HSMT, c Ville Leppänen, IT, Turun yliopisto, 2009 p.1/15 HSMT (Java-kielellä) Aineopintotasoinen kurssi, 5op. Luennot:

Lisätiedot

Luento 6. T Ohjelmoinnin jatkokurssi T1 & T Ohjelmoinnin jatkokurssi L1. Luennoitsija: Otto Seppälä

Luento 6. T Ohjelmoinnin jatkokurssi T1 & T Ohjelmoinnin jatkokurssi L1. Luennoitsija: Otto Seppälä Luento 6 T-106.1240 Ohjelmoinnin jatkokurssi T1 & T-106.1243 Ohjelmoinnin jatkokurssi L1 Luennoitsija: Otto Seppälä Kurssin WWW: http://www.cs.hut.fi/opinnot/t-106.1240/s2007 Oma Grafiikka Swing-käyttöliittymässä

Lisätiedot

Concurrency - Rinnakkaisuus. Group: 9 Joni Laine Juho Vähätalo

Concurrency - Rinnakkaisuus. Group: 9 Joni Laine Juho Vähätalo Concurrency - Rinnakkaisuus Group: 9 Joni Laine Juho Vähätalo Sisällysluettelo 1. Johdanto... 3 2. C++ thread... 4 3. Python multiprocessing... 6 4. Java ExecutorService... 8 5. Yhteenveto... 9 6. Lähteet...

Lisätiedot

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä Ohjelmoinnin peruskurssien laaja oppimäärä Luento 19: Rinnakkaisuus Riku Saikkonen (merkityt ei-laajan kurssin kalvot: Otto Seppälä) 24. 3. 2011 Sisältö 1 Säikeet 2 Lukot 3 Monitorit 4 Muuta säikeisiin

Lisätiedot

Rinnakkaisohjelmointi kurssi. Opintopiiri työskentelyn raportti

Rinnakkaisohjelmointi kurssi. Opintopiiri työskentelyn raportti Rinnakkaisohjelmointi kurssi Opintopiiri työskentelyn raportti Opintopiiri: Heikki Karimo, Jesse Paakkari ja Keijo Karhu Päiväys: 15.12.2006 Ohjelmointitehtävä C i C i : Säikeet ja kriittisen vaiheen kontrollointi

Lisätiedot

Projekti 1 Säikeet ja kriittisen vaiheen kontrollointi javalla

Projekti 1 Säikeet ja kriittisen vaiheen kontrollointi javalla Projekti 1 Säikeet ja kriittisen vaiheen kontrollointi javalla Lasse Leino ja Marko Kahilakoski Helsingin Yliopisto Tietojenkäsittelytieteen laitos Rinnakkaisohjelmointi 18. joulukuuta 2006 Sisältö 1 Säikeet

Lisätiedot

Liite 1. Projektin tulokset (Semaforit Javassa) Jukka Hyvärinen Aleksanteri Aaltonen

Liite 1. Projektin tulokset (Semaforit Javassa) Jukka Hyvärinen Aleksanteri Aaltonen Helsingin Yliopisto, tietojenkäsittelytieteen laitos Rinnakkaisohjelmointi (syksy 2006) Liite 1. Projektin tulokset (Semaforit Javassa) Jukka Hyvärinen Aleksanteri Aaltonen a. Käyttötarkoitus ja sovellusalue

Lisätiedot

Monitorit -projekti Rinnakkaisohjelmointi

Monitorit -projekti Rinnakkaisohjelmointi Monitorit -projekti Rinnakkaisohjelmointi 13.12.2006 Jaakko Louhio, Lari Sorvo Projektin tuloksia, kertaustehtäviä ja koodeja saa käyttää opetukseen yliopistolla vapaasti omalla vastuulla. 3. MONITORIT

Lisätiedot

812315A Ohjelmiston rakentaminen. Asynkronisuus

812315A Ohjelmiston rakentaminen. Asynkronisuus 812315A Ohjelmiston rakentaminen. Asynkronisuus Ari Vesanen ari.vesanen (at) oulu.fi Yleistä moduulista Tällä kertaa sisältää Java-kielistä monisäieohjelmointia Suoritustapa: Neljästä ohjelmointitehtävästä

Lisätiedot

Käyttöjärjestelmät: poissulkeminen ja synkronointi

Käyttöjärjestelmät: poissulkeminen ja synkronointi Käyttöjärjestelmät: poissulkeminen ja synkronointi Teemu Saarelainen Tietotekniikka teemu.saarelainen@kyamk.fi Lähteet Stallings, W. Operating Systems Haikala, Järvinen, Käyttöjärjestelmät Eri Web-lähteet

Lisätiedot

12. Javan toistorakenteet 12.1

12. Javan toistorakenteet 12.1 12. Javan toistorakenteet 12.1 Sisällys Yleistä toistorakenteista. Laskurimuuttujat. While-, do-while- ja for-lauseet. Laskuri- ja lippumuuttujat. Tyypillisiä ohjelmointivirheitä. Silmukan rajat asetettu

Lisätiedot

11. Javan toistorakenteet 11.1

11. Javan toistorakenteet 11.1 11. Javan toistorakenteet 11.1 Sisällys Laskuri- ja lippumuuttujat. Sisäkkäiset silmukat. Tyypillisiä ohjelmointivirheitä: Silmukan rajat asetettu kierroksen verran väärin. Ikuinen silmukka. Silmukoinnin

Lisätiedot

JAVA on ohjelmointikieli, mikä on kieliopiltaan hyvin samankaltainen, jopa identtinen mm. C++

JAVA on ohjelmointikieli, mikä on kieliopiltaan hyvin samankaltainen, jopa identtinen mm. C++ JAVA alkeet JAVA on ohjelmointikieli, mikä on kieliopiltaan hyvin samankaltainen, jopa identtinen mm. C++ ja Javascriptin kanssa. Huom! JAVA ja JavaScript eivät silti ole sama asia, eivätkä edes sukulaiskieliä.

Lisätiedot

Graafisen käyttöliittymän ohjelmointi Syksy 2013

Graafisen käyttöliittymän ohjelmointi Syksy 2013 TIE-11300 Tietotekniikan vaihtuva-alainen kurssi Graafisen käyttöliittymän ohjelmointi Syksy 2013 Luento 10 Rinnakkaisuus käyttöliittymäohjelmoinnissa Juha-Matti Vanhatupa Rinnakkaisuus ja käyttöliittymäohjelmointi

Lisätiedot

HSMT J2EE & EJB & SOAP &...

HSMT J2EE & EJB & SOAP &... HSMT J2EE & EJB & SOAP &... Ville Leppänen HSMT, c Ville Leppänen, IT, Turun yliopisto, 2011 p.1/15 Missä mennään... 1. Johdanto (1h) 2. Säikeet (2h) 3. Samanaikaisuudesta (2h) 4. Hajautetuista sovelluksista

Lisätiedot

HOJ Haja-aiheita. Ville Leppänen. HOJ, c Ville Leppänen, IT, Turun yliopisto, 2012 p.1/10

HOJ Haja-aiheita. Ville Leppänen. HOJ, c Ville Leppänen, IT, Turun yliopisto, 2012 p.1/10 HOJ Haja-aiheita Ville Leppänen HOJ, c Ville Leppänen, IT, Turun yliopisto, 2012 p.1/10 Missä mennään... 1. Johdanto (1h) 2. Säikeet (2h) 3. Samanaikaisuudesta (2h) 4. Hajautetuista sovelluksista (1h)

Lisätiedot

Ohjelmoinnin jatkokurssi, kurssikoe 28.4.2014

Ohjelmoinnin jatkokurssi, kurssikoe 28.4.2014 Ohjelmoinnin jatkokurssi, kurssikoe 28.4.2014 Kirjoita jokaiseen palauttamaasi konseptiin kurssin nimi, kokeen päivämäärä, oma nimi ja opiskelijanumero. Vastaa kaikkiin tehtäviin omille konsepteilleen.

Lisätiedot

Rinnakkaisuus. parallel tietokoneissa rinnakkaisia laskentayksiköitä concurrent asioita tapahtuu yhtaikaa. TTY Ohjelmistotekniikka

Rinnakkaisuus. parallel tietokoneissa rinnakkaisia laskentayksiköitä concurrent asioita tapahtuu yhtaikaa. TTY Ohjelmistotekniikka Rinnakkaisuus parallel tietokoneissa rinnakkaisia laskentayksiköitä concurrent asioita tapahtuu yhtaikaa Rinnakkaisuuden etuja: laskennan nopeutuminen (sarjoittuvat operaatiojonot) ilmaisuvoima (ongelman

Lisätiedot

Ohjelmassa henkilön etunimi ja sukunimi luetaan kahteen muuttujaan seuraavasti:

Ohjelmassa henkilön etunimi ja sukunimi luetaan kahteen muuttujaan seuraavasti: 1 (7) Tiedon lukeminen näppäimistöltä Scanner-luokan avulla Miten ohjelma saa käyttöönsä käyttäjän kirjoittamaa tekstiä? Järjestelmässä on olemassa ns. syöttöpuskuri näppäimistöä varten. Syöttöpuskuri

Lisätiedot

Rinnakkaisohjelmointi, Syksy 2006

Rinnakkaisohjelmointi, Syksy 2006 Rinnakkaisohjelmointi, Syksy 2006 17.12.2006 Opintopiiri WTF Mika Holmström Paula Kemppi Janne Piippo Lasse Lukkari Javan semaforit 1. Menetelmän käyttötarkoitus ja sovellusalue Semaforin idea kehitettiin

Lisätiedot

14. Poikkeukset 14.1

14. Poikkeukset 14.1 14. Poikkeukset 14.1 Sisällys Johdanto. Tarkistettavat ja tarkistamattomat poikkeukset. Poikkeusten tunnistaminen ja sieppaaminen try-catchlauseella. Mitä tehdä siepatulla poikkeuksella? Poikkeusten heittäminen.

Lisätiedot

JAVA-PERUSTEET. JAVA-OHJELMOINTI 3op A274615 JAVAN PERUSTEET LYHYT KERTAUS JAVAN OMINAISUUKSISTA JAVAN OMINAISUUKSIA. Java vs. C++?

JAVA-PERUSTEET. JAVA-OHJELMOINTI 3op A274615 JAVAN PERUSTEET LYHYT KERTAUS JAVAN OMINAISUUKSISTA JAVAN OMINAISUUKSIA. Java vs. C++? JAVA-OHJELMOINTI 3op A274615 JAVAN PERUSTEET LYHYT KERTAUS Teemu Saarelainen teemu.saarelainen@kyamk.fi Lähteet: http://java.sun.com/docs/books/tutorial/index.html Vesterholm, Kyppö: Java-ohjelmointi,

Lisätiedot

Ohjelmointitaito (ict1td002, 12 op) Kevät 2008. 1. Java-ohjelmoinnin alkeita. Tietokoneohjelma. Raine Kauppinen raine.kauppinen@haaga-helia.

Ohjelmointitaito (ict1td002, 12 op) Kevät 2008. 1. Java-ohjelmoinnin alkeita. Tietokoneohjelma. Raine Kauppinen raine.kauppinen@haaga-helia. Ohjelmointitaito (ict1td002, 12 op) Kevät 2008 Raine Kauppinen raine.kauppinen@haaga-helia.fi 1. Java-ohjelmoinnin alkeita Tietokoneohjelma Java-kieli ja Eclipse-ympäristö Java-ohjelma ja ohjelmaluokka

Lisätiedot

Informaatioteknologian laitos Olio-ohjelmoinnin perusteet / Salo 15.2.2006

Informaatioteknologian laitos Olio-ohjelmoinnin perusteet / Salo 15.2.2006 TURUN YLIOPISTO DEMO III Informaatioteknologian laitos tehtävät Olio-ohjelmoinnin perusteet / Salo 15.2.2006 1. Tässä tehtävässä tarkastellaan erääntyviä laskuja. Lasku muodostaa oman luokkansa. Laskussa

Lisätiedot

Sisällys. 14. Poikkeukset. Johdanto. Johdanto

Sisällys. 14. Poikkeukset. Johdanto. Johdanto Sisällys 14. Poikkeukset Johdanto. Tarkistettavat ja tarkistamattomat poikkeukset. Poikkeusten tunnistaminen ja sieppaaminen try-catchlauseella. Mitä tehdä siepatulla poikkeuksella? Poikkeusten heittäminen.

Lisätiedot

4. Luento: Prosessit ja säikeets. Tommi Mikkonen, tommi.mikkonen@tut.fi

4. Luento: Prosessit ja säikeets. Tommi Mikkonen, tommi.mikkonen@tut.fi 4. Luento: Prosessit ja säikeets Tommi Mikkonen, tommi.mikkonen@tut.fi Agenda Prosessi Säikeet Keskeytykset Keskeytyskäsittely Käyttöjärjestelmäkutsut Prosessielementti Prosessin hallinta Suunnittelunäkökohtia

Lisätiedot

Sisällys. 11. Javan toistorakenteet. Laskurimuuttujat. Yleistä

Sisällys. 11. Javan toistorakenteet. Laskurimuuttujat. Yleistä Sisällys 11. Javan toistorakenteet Laskuri- ja lippumuuttujat.. Tyypillisiä ohjelmointivirheitä: Silmukan rajat asetettu kierroksen verran väärin. Ikuinen silmukka. Silmukoinnin lopettaminen break-lauseella.

Lisätiedot

Lohkot. if (ehto1) { if (ehto2) { lause 1;... lause n; } } else { lause 1;... lause m; } 15.3

Lohkot. if (ehto1) { if (ehto2) { lause 1;... lause n; } } else { lause 1;... lause m; } 15.3 15. Lohkot 15.1 Sisällys Tutustutaan lohkoihin. Muuttujien ja vakioiden näkyvyys sekä elinikä erityisesti operaation lohkossa. Nimikonfliktit. Muuttujat operaation alussa vai myöhemmin? 15.2 Lohkot Aaltosulkeet

Lisätiedot

12. Javan toistorakenteet 12.1

12. Javan toistorakenteet 12.1 12. Javan toistorakenteet 12.1 Sisällys Yleistä toistorakenteista. Laskurimuuttujat. While-, do-while- ja for-lauseet. Laskuri- ja lippumuuttujat. Tyypillisiä ohjelmointivirheitä. Silmukan rajat asetettu

Lisätiedot

1 Tehtävän kuvaus ja analysointi

1 Tehtävän kuvaus ja analysointi Olio-ohjelmoinnin harjoitustyön dokumentti Jyri Lehtonen (72039) Taneli Tuovinen (67160) 1 Tehtävän kuvaus ja analysointi 1.1 Tehtävänanto Tee luokka, jolla mallinnetaan sarjaan kytkettyjä kondensaattoreita.

Lisätiedot

Yleistä. Nyt käsitellään vain taulukko (array), joka on saman tyyppisten muuttujien eli alkioiden (element) kokoelma.

Yleistä. Nyt käsitellään vain taulukko (array), joka on saman tyyppisten muuttujien eli alkioiden (element) kokoelma. 2. Taulukot 2.1 Sisältö Yleistä. Esittely ja luominen. Alkioiden käsittely. Kaksiulotteinen taulukko. Taulukko operaation parametrina. Taulukko ja HelloWorld-ohjelma. Taulukko paluuarvona. 2.2 Yleistä

Lisätiedot

Metodit. Metodien määrittely. Metodin parametrit ja paluuarvo. Metodien suorittaminen eli kutsuminen. Metodien kuormittaminen

Metodit. Metodien määrittely. Metodin parametrit ja paluuarvo. Metodien suorittaminen eli kutsuminen. Metodien kuormittaminen Metodit Metodien määrittely Metodin parametrit ja paluuarvo Metodien suorittaminen eli kutsuminen Metodien kuormittaminen 1 Mikä on metodi? Metodi on luokan sisällä oleva yhteenkuuluvien toimintojen kokonaisuus

Lisätiedot

9. Periytyminen Javassa 9.1

9. Periytyminen Javassa 9.1 9. Periytyminen Javassa 9.1 Sisällys Periytymismekanismi Java-kielessä. Piirteiden näkyvyys periytymisessä. Ilmentymämetodien korvaaminen. Luokkametodien peittäminen. Super-attribuutti. Override-annotaatio.

Lisätiedot

Sisältö. 2. Taulukot. Yleistä. Yleistä

Sisältö. 2. Taulukot. Yleistä. Yleistä Sisältö 2. Taulukot Yleistä. Esittely ja luominen. Alkioiden käsittely. Kaksiulotteinen taulukko. Taulukko operaation parametrina. Taulukko ja HelloWorld-ohjelma. Taulukko paluuarvona. 2.1 2.2 Yleistä

Lisätiedot

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä Ohjelmoinnin peruskurssien laaja oppimäärä Luento 11: Rinnakkaisuus Riku Saikkonen (osa kalvoista on suoraan ei-laajan kurssin luennoista) 25. 4. 2012 Sisältö 1 Rinnakkaisuusmalleja: säie ja prosessi 2

Lisätiedot

HOJ J2EE & EJB & SOAP &...

HOJ J2EE & EJB & SOAP &... HOJ J2EE & EJB & SOAP &... Ville Leppänen HOJ, c Ville Leppänen, IT, Turun yliopisto, 2012 p.1/18 Missä mennään... 1. Johdanto (1h) 2. Säikeet (2h) 3. Samanaikaisuudesta (2h) 4. Hajautetuista sovelluksista

Lisätiedot

Sisältö. 22. Taulukot. Yleistä. Yleistä

Sisältö. 22. Taulukot. Yleistä. Yleistä Sisältö 22. Taulukot Yleistä. Esittely ja luominen. Alkioiden käsittely. Kaksiulotteinen taulukko. Taulukko metodin parametrina. Taulukko ja HelloWorld-ohjelma. Taulukko paluuarvona. 22.1 22.2 Yleistä

Lisätiedot

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin perusteet Y Python Ohjelmoinnin perusteet Y Python T-106.1208 15.3.2010 T-106.1208 Ohjelmoinnin perusteet Y 15.3.2010 1 / 56 Tiedostoista: tietojen tallentaminen ohjelman suorituskertojen välillä Monissa sovelluksissa ohjelman

Lisätiedot

1. Mitä tehdään ensiksi?

1. Mitä tehdään ensiksi? 1. Mitä tehdään ensiksi? Antti Jussi i Lakanen Ohjelmointi 1, kevät 2010/ Jyväskylän yliopisto a) Etsitään Googlesta valmis algoritmi b) Mietitään miten itse tehtäisiin sama homma kynällä ja paperilla

Lisätiedot

Lohkot. if (ehto1) { if (ehto2) { lause 1;... lause n; } } else { lause 1;... lause m; } 16.3

Lohkot. if (ehto1) { if (ehto2) { lause 1;... lause n; } } else { lause 1;... lause m; } 16.3 16. Lohkot 16.1 Sisällys Tutustutaan lohkoihin. Muuttujien ja vakioiden näkyvyys sekä elinikä erityisesti operaation lohkossa. Nimikonfliktit. Muuttujat operaation alussa vai myöhemmin? 16.2 Lohkot Kaarisulut

Lisätiedot

812341A Olio-ohjelmointi Peruskäsitteet jatkoa

812341A Olio-ohjelmointi Peruskäsitteet jatkoa 812341A Olio-ohjelmointi 2106 Peruskäsitteet jatkoa Luokkakohtaiset piirteet n Yhteisiä kaikille saman luokan olioille n Liittyvät luokkaan, eivät yksittäiseen olioon n Kaikki ko. luokan oliot voivat käyttää

Lisätiedot

Rajapinta (interface)

Rajapinta (interface) 1 Rajapinta (interface) Mikä rajapinta on? Rajapinta ja siitä toteutettu luokka Monimuotoisuus ja dynaaminen sidonta Rajapinta vs periytyminen 1 Mikä rajapinta on? Rajapintoja käytetään, kun halutaan määritellä

Lisätiedot

Sisällys. 15. Lohkot. Lohkot. Lohkot

Sisällys. 15. Lohkot. Lohkot. Lohkot Sisällys 15. Lohkot Tutustutaan lohkoihin. Muuttujien ja vakioiden näkyvyys sekä elinikä erityisesti operaation lohkossa. Nimikonfliktit. Muuttujat operaation alussa vai myöhemmin? 15.1 15.2 Lohkot Aaltosulkeet

Lisätiedot

Olio-ohjelmointi Javalla

Olio-ohjelmointi Javalla 1 Olio-ohjelmointi Javalla Olio-ohjelmointi Luokka Attribuutit Konstruktori Olion luominen Metodit Olion kopiointi Staattinen attribuutti ja metodi Yksinkertainen ohjelmaluokka Ohjelmaluokka 1 Olio-ohjelmointi

Lisätiedot

Sisällys. Metodien kuormittaminen. Luokkametodit ja -attribuutit. Rakentajat. Metodien ja muun luokan sisällön järjestäminen. 6.2

Sisällys. Metodien kuormittaminen. Luokkametodit ja -attribuutit. Rakentajat. Metodien ja muun luokan sisällön järjestäminen. 6.2 6. Metodit 6.1 Sisällys Metodien kuormittaminen. Luokkametodit ja -attribuutit. Rakentajat. Metodien ja muun luokan sisällön järjestäminen. 6.2 Oliot viestivät metodeja kutsuen Olio-ohjelmoinnissa ohjelma

Lisätiedot

11/20: Konepelti auki

11/20: Konepelti auki Ohjelmointi 1 / syksy 2007 11/20: Konepelti auki Paavo Nieminen nieminen@jyu.fi Tietotekniikan laitos Informaatioteknologian tiedekunta Jyväskylän yliopisto Ohjelmointi 1 / syksy 2007 p.1/11 Tämän luennon

Lisätiedot

Sisällys. 14. Poikkeukset. Johdanto. Johdanto

Sisällys. 14. Poikkeukset. Johdanto. Johdanto Sisällys 14. Poikkeukset Johdanto. Tarkistettavat ja tarkistamattomat poikkeukset. Miten varautua poikkeukseen metodissa? Poikkeusten tunnistaminen ja sieppaaminen try-catchlauseella. Mitä tehdä siepatulla

Lisätiedot

10 Lock Lock-lause

10 Lock Lock-lause 10 Lock Avainsanan lock (Lock keyword) avulla voidaan toteuttaa prosessien ja säikeiden välisessä keskinäisessä kommunikoinnissa käytettäviä synkronointi- ja poissuljentarakenteita. Tämän niin sanotun

Lisätiedot

Javan perusteita. Janne Käki

Javan perusteita. Janne Käki Javan perusteita Janne Käki 20.9.2006 Muutama perusasia Tietokone tekee juuri (ja vain) sen, mitä käsketään. Tietokone ymmärtää vain syntaksia (sanojen kirjoitusasua), ei semantiikkaa (sanojen merkitystä).

Lisätiedot

Oppimistavoitteet kurssilla Rinnakkaisohjelmointi

Oppimistavoitteet kurssilla Rinnakkaisohjelmointi 17.5.2006 1/5 Oppimistavoitteet kurssilla Rinnakkaisohjelmointi Rinnakkaisuus ja rinnakkaisuuden soveltaminen tietojenkäsittelyjärjestelmissä Kurssin Tietokoneen toiminta perusteella ymmärtää, miten ohjelman

Lisätiedot

5. Luento: Rinnakkaisuus ja reaaliaika. Tommi Mikkonen, tommi.mikkonen@tut.fi

5. Luento: Rinnakkaisuus ja reaaliaika. Tommi Mikkonen, tommi.mikkonen@tut.fi 5. Luento: Rinnakkaisuus ja reaaliaika Tommi Mikkonen, tommi.mikkonen@tut.fi Agenda Perusongelmat Jako prosesseihin Reaaliaika Rinnakkaisuus Rinnakkaisuus tarkoittaa tässä yhteydessä useamman kuin yhden

Lisätiedot

815338A Ohjelmointikielten periaatteet

815338A Ohjelmointikielten periaatteet 815338A Ohjelmointikielten periaatteet 2015-2016 IX Rinnakkainen ohjelmointi Sisältö 1. Yleistä rinnakkaisuudesta 2. Prosesseista ja säikeistä 3. Rinnakkaisen ohjelman oikeellisuudesta 4. Rinnakkaisuuden

Lisätiedot

P e d a c o d e ohjelmointikoulutus verkossa

P e d a c o d e ohjelmointikoulutus verkossa P e d a c o d e ohjelmointikoulutus verkossa Java-kielen perusteet Teoria ja ohjelmointitehtävät Java-kielen perusteet 3 YLEISKATSAUS KURSSIN SISÄLTÖIHIN 10 JAVA-KIELEN PERUSTEET 10 OPISKELUN ALOITTAMINEN

Lisätiedot

Poikkeustenkäsittely

Poikkeustenkäsittely 1 Poikkeustenkäsittely Mitä poikkeustenkäsittely tarkoittaa? Poikkeuksen käsitteleminen Poikkeusluokkien hierarkia Poikkeuksen heittäminen 1 Mitä poikkeustenkäsittely tarkoittaa? Poikkeus (Exception) on

Lisätiedot

Harjoitus 7. 1. Olkoon olemassa luokat Lintu ja Pelikaani seuraavasti:

Harjoitus 7. 1. Olkoon olemassa luokat Lintu ja Pelikaani seuraavasti: Harjoitus 7 1. Olkoon olemassa luokat Lintu ja Pelikaani seuraavasti: class Lintu //Kentät private int _siivenpituus; protected double _aivojenkoko; private bool _osaakolentaa; //Ominaisuudet public int

Lisätiedot

Sisällys. 6. Metodit. Oliot viestivät metodeja kutsuen. Oliot viestivät metodeja kutsuen

Sisällys. 6. Metodit. Oliot viestivät metodeja kutsuen. Oliot viestivät metodeja kutsuen Sisällys 6. Metodit Oliot viestivät metodeja kutsuen. Kuormittaminen. Luokkametodit (ja -attribuutit).. Metodien ja muun luokan sisällön järjestäminen. 6.1 6.2 Oliot viestivät metodeja kutsuen Oliot viestivät

Lisätiedot

14. Poikkeukset 14.1

14. Poikkeukset 14.1 14. Poikkeukset 14.1 Sisällys Johdanto. Tarkistettavat ja tarkistamattomat poikkeukset. Miten varautua poikkeukseen metodissa? Poikkeusten tunnistaminen ja sieppaaminen try-catchlauseella. Mitä tehdä siepatulla

Lisätiedot

T Henkilökohtainen harjoitus: FASTAXON

T Henkilökohtainen harjoitus: FASTAXON T-76.115 Henkilökohtainen harjoitus: FASTAXON Suunnittelumallit Group: Muuntaja Pentti Vänskä 52572W 2 1. Toteutus Tämä henkilökohtainen harjoitustyö käsitteli suunnittelumallien (Design Patterns) käyttöä

Lisätiedot

Ohjelmointi 2 / 2008 Välikoe / Pöytätestaa seuraava ohjelma.

Ohjelmointi 2 / 2008 Välikoe / Pöytätestaa seuraava ohjelma. Välikoe / 20.3 Vastaa neljään (4) tehtävään. Jos vastaat 5:een, 4 huonointa arvostellaan. Kunkin tehtävän vastaus eri konseptille. 1. Pöytätesti Pöytätestaa seuraava ohjelma. Tutki ohjelman toimintaa pöytätestillä

Lisätiedot

2 Konekieli, aliohjelmat, keskeytykset

2 Konekieli, aliohjelmat, keskeytykset ITK145 Käyttöjärjestelmät, kesä 2005 Tenttitärppejä Tässä on lueteltu suurin piirtein kaikki vuosina 2003-2005 kurssin tenteissä kysytyt kysymykset, ja mukana on myös muutama uusi. Jokaisessa kysymyksessä

Lisätiedot

Sisällys. 12. Näppäimistöltä lukeminen. Yleistä. Yleistä 12.1 12.2 12.3 12.4

Sisällys. 12. Näppäimistöltä lukeminen. Yleistä. Yleistä 12.1 12.2 12.3 12.4 Sisällys 12. Näppäimistöltä lukeminen Arvojen lukeminen näppäimistöltä yleisesti. Arvojen lukeminen näppäimistöltä Java-kielessä.. Luetun arvon tarkistaminen. Tietovirrat ja ohjausmerkit. Scanner-luokka.

Lisätiedot

Taulukot. Jukka Harju, Jukka Juslin 2006 1

Taulukot. Jukka Harju, Jukka Juslin 2006 1 Taulukot Jukka Harju, Jukka Juslin 2006 1 Taulukot Taulukot ovat olioita, jotka auttavat organisoimaan suuria määriä tietoa. Käsittelylistalla on: Taulukon tekeminen ja käyttö Rajojen tarkastus ja kapasiteetti

Lisätiedot

Oliosuunnitteluesimerkki: Yrityksen palkanlaskentajärjestelmä

Oliosuunnitteluesimerkki: Yrityksen palkanlaskentajärjestelmä Oliosuunnitteluesimerkki: Yrityksen palkanlaskentajärjestelmä Matti Luukkainen 10.12.2009 Tässä esitetty esimerkki on mukaelma ja lyhennelmä Robert Martinin kirjasta Agile and Iterative Development löytyvästä

Lisätiedot

Ohjelmointitaito (ict1td002, 12 op) Kevät Java-ohjelmoinnin alkeita. Tietokoneohjelma. Raine Kauppinen

Ohjelmointitaito (ict1td002, 12 op) Kevät Java-ohjelmoinnin alkeita. Tietokoneohjelma. Raine Kauppinen Ohjelmointitaito (ict1td002, 12 op) Kevät 2009 Raine Kauppinen raine.kauppinen@haaga-helia.fi 1. Java-ohjelmoinnin alkeita Tietokoneohjelma Java-kieli ja Eclipse-kehitysympäristö Java-ohjelma ja luokka

Lisätiedot

Java-kielen perusteet

Java-kielen perusteet Java-kielen perusteet Tunnus, varattu sana, kommentti Muuttuja, alkeistietotyyppi, merkkijono, literaalivakio, nimetty vakio Tiedon merkkipohjainen tulostaminen 1 Tunnus Java tunnus Java-kirjain Java-numero

Lisätiedot

Java kahdessa tunnissa. Jyry Suvilehto

Java kahdessa tunnissa. Jyry Suvilehto Java kahdessa tunnissa Jyry Suvilehto Ohjelma Ohjelmointiasioita alkeista nippelitietoon n. 45 min Tauko 10 min Oliot, luokat ja muut kummajaiset n. 45 min Kysykää Sisältöä ei oikeasti ole 2x45 min täytteeksi,

Lisätiedot

Javan semaforit. Joel Rybicki, Aleksi Nur mi, Jara Uitto. Helsingin yliopisto

Javan semaforit. Joel Rybicki, Aleksi Nur mi, Jara Uitto. Helsingin yliopisto Javan semaforit Joel Rybicki, Aleksi Nur mi, Jara Uitto 16.12.2007 Helsingin yliopisto Tietojenkäsittelytieteen laitos Tätä ohjetta saa käyttää ja jatkokehittää opetustarkoituksiin. Javan semaforitoteutus

Lisätiedot

Palvelut. Sulautetut järjestelmät Luku 2 Sivu 1 (??) Sulautetut käyttöjärjestelmät

Palvelut. Sulautetut järjestelmät Luku 2 Sivu 1 (??) Sulautetut käyttöjärjestelmät Sulautetut järjestelmät Luku 2 Sivu 1 (??) Palvelut Käyttöjärjestelmän tehtävänä on tarjota ohjelmoijalla erilaisia palveluita Tyypillisin palvelu, jota sulautetut käyttöjärjestelmät tarjoavat on prosessien

Lisätiedot

Opintojakso TT00AA11 Ohjelmoinnin jatko (Java): 3 op. Poikkeukset ja tietovirrat: Virhetilanteiden ja syötevirtojen käsittely

Opintojakso TT00AA11 Ohjelmoinnin jatko (Java): 3 op. Poikkeukset ja tietovirrat: Virhetilanteiden ja syötevirtojen käsittely Opintojakso TT00AA11 Ohjelmoinnin jatko (Java): 3 op Poikkeukset ja tietovirrat: Virhetilanteiden ja syötevirtojen käsittely Poikkeukset Poikkeuksella tarkoitetaan yllättävää ajonaikaista tilannetta, joka

Lisätiedot

Vertailulauseet. Ehtolausekkeet. Vertailulauseet. Vertailulauseet. if-lauseke. if-lauseke. Javan perusteet 2004

Vertailulauseet. Ehtolausekkeet. Vertailulauseet. Vertailulauseet. if-lauseke. if-lauseke. Javan perusteet 2004 Vertailulauseet Ehtolausekkeet Ehdot, valintalausekkeet Boolean-algebra == yhtäsuuruus!= erisuuruus < pienempi suurempi >= suurempi tai yhtäsuuri Esimerkkejä: int i=7; int j=10;

Lisätiedot

Java-kielen perusteita

Java-kielen perusteita Java-kielen perusteita Käyttäjän kanssa keskusteleva ohjelma 1 Kirjoittaminen konsolinäkymään //Java ohjelma, joka tulostaa konsoli-ikkunaan public class HeiMaailma { public void aja() { // kirjoitus ja

Lisätiedot

Se mistä tilasta aloitetaan, merkitään tyhjästä tulevalla nuolella. Yllä olevassa esimerkissä aloitustila on A.

Se mistä tilasta aloitetaan, merkitään tyhjästä tulevalla nuolella. Yllä olevassa esimerkissä aloitustila on A. Tehtävä. Tämä tehtävä on aineistotehtävä, jossa esitetään ensin tehtävän teoria. Sen jälkeen esitetään neljä kysymystä, joissa tätä teoriaa pitää soveltaa. Mitään aikaisempaa tehtävän aihepiirin tuntemusta

Lisätiedot

Tietotekniikan valintakoe

Tietotekniikan valintakoe Jyväskylän yliopisto Tietotekniikan laitos Tietotekniikan valintakoe 2..22 Vastaa kahteen seuraavista kolmesta tehtävästä. Kukin tehtävä arvostellaan kokonaislukuasteikolla - 25. Jos vastaat useampaan

Lisätiedot

Alkuarvot ja tyyppimuunnokset (1/5) Alkuarvot ja tyyppimuunnokset (2/5) Alkuarvot ja tyyppimuunnokset (3/5)

Alkuarvot ja tyyppimuunnokset (1/5) Alkuarvot ja tyyppimuunnokset (2/5) Alkuarvot ja tyyppimuunnokset (3/5) Alkuarvot ja tyyppimuunnokset (1/5) Aiemmin olemme jo antaneet muuttujille alkuarvoja, esimerkiksi: int luku = 123; Alkuarvon on oltava muuttujan tietotyypin mukainen, esimerkiksi int-muuttujilla kokonaisluku,

Lisätiedot

Opintojakso TT00AA11 Ohjelmoinnin jatko (Java): 3 op Taulukot & Periytyminen

Opintojakso TT00AA11 Ohjelmoinnin jatko (Java): 3 op Taulukot & Periytyminen Opintojakso TT00AA11 Ohjelmoinnin jatko (Java): 3 op Taulukot & Periytyminen Taulukot: Array Taulukko Javassa pitää aina perustaa (new) Yksinkertaisessa tilanteessa taulukon koko tiedetään etukäteen ja

Lisätiedot

812347A Olio-ohjelmointi, 2015 syksy 2. vsk. X Poikkeusten käsittelystä

812347A Olio-ohjelmointi, 2015 syksy 2. vsk. X Poikkeusten käsittelystä 812347A Olio-ohjelmointi, 2015 syksy 2. vsk X Poikkeusten käsittelystä Sisältö 1. Yleistä poikkeusten käsittelystä 2. Poikkeuskäsittelyn perusteita C++:ssa 3. Standardissa määritellyt poikkeukset 4. Poikkeusvarmuus

Lisätiedot

Sisällys. 12. Javan toistorakenteet. Yleistä. Laskurimuuttujat

Sisällys. 12. Javan toistorakenteet. Yleistä. Laskurimuuttujat Sisällys 12. Javan toistorakenteet Ylstä toistorakentsta. Laskurimuuttujat. While-, do-while- ja for-lauseet. Laskuri- ja lippumuuttujat. Tyypillisiä ohjelmointivirhtä. Silmukan rajat asetettu kierroksen

Lisätiedot

on ohjelmoijan itse tekemä tietotyyppi, joka kuvaa käsitettä

on ohjelmoijan itse tekemä tietotyyppi, joka kuvaa käsitettä LUOKAN MÄÄRITTELY Luokka, mitä se sisältää Luokan määrittely Olion ominaisuudet eli attribuutit Olion metodit Olion muodostimet ja luonti Olion tuhoutuminen Metodin kutsu luokan ulkopuolelta Olion kopioiminen

Lisätiedot

Mikä yhteyssuhde on?

Mikä yhteyssuhde on? 1 Yhteyssuhde Mikä yhteyssuhde on? Yhteyssuhde Javalla Konstruktorit set-ja get-metodit tostring-metodi Pääohjelma 1 Mikä yhteyssuhde on? Tili - : String - : double * 1 Asiakas - hetu: String - : String

Lisätiedot

Ohjelmoinnin perusteet, kurssikoe

Ohjelmoinnin perusteet, kurssikoe Ohjelmoinnin perusteet, kurssikoe 18.6.2014 Kirjoita jokaiseen konseptiin kurssin nimi, kokeen päivämäärä, nimi, TMC-tunnus ja opiskelijanumero tai henkilötunnus. Vastaukset palautetaan tehtäväkohtaisiin

Lisätiedot

Jaana Diakite Projekti 1 JAVA-Monitorit 1(13) Rinnakkaisohjelmointi Anu Uusitalo

Jaana Diakite Projekti 1 JAVA-Monitorit 1(13) Rinnakkaisohjelmointi Anu Uusitalo Jaana Diakite Projekti 1 JAVA-Monitorit 1(13) JAVA MONITORIT Monitorien käyttötarkoitus ja sovellusalue Monitorit ovat Java ohjelmointikielen sisäänrakennettu mekanismi säikeiden rinnakkaisuuden hallintaan.

Lisätiedot

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin perusteet Y Python Ohjelmoinnin perusteet Y Python T-106.1208 2.3.2009 T-106.1208 Ohjelmoinnin perusteet Y 2.3.2009 1 / 28 Puhelinluettelo, koodi def lue_puhelinnumerot(): print "Anna lisattavat nimet ja numerot." print

Lisätiedot

Java-kielen perusteita

Java-kielen perusteita Java-kielen perusteita valintalauseet 1 Johdantoa kontrollirakenteisiin Tähän saakka ohjelmissa on ollut vain peräkkäisyyttä eli lauseet on suoritettu peräkkäin yksi kerrallaan Tarvitsemme myös valintaa

Lisätiedot

Ohjelmistojen mallintaminen, sekvenssikaaviot

Ohjelmistojen mallintaminen, sekvenssikaaviot 582104 - Ohjelmistojen mallintaminen, sekvenssikaaviot 1 Vuorovaikutussuunnittelu Oliojärjestelmän toiminta perustuu olioiden vuorovaikutukseen ja yhteistyöhön Olioiden yhteistyö toteutuu operaatioiden

Lisätiedot

Ohjelmointi 2 / 2010 Välikoe / 26.3

Ohjelmointi 2 / 2010 Välikoe / 26.3 Ohjelmointi 2 / 2010 Välikoe / 26.3 Välikoe / 26.3 Vastaa neljään (4) tehtävään ja halutessa bonustehtäviin B1 ja/tai B2, (tuovat lisäpisteitä). Bonustehtävät saa tehdä vaikkei olisi tehnyt siihen tehtävään

Lisätiedot

JavaRMI 1 JAVA RMI. Rinnakkaisohjelmoinnin projekti 1 osa C Tekijät: Taru Itäpelto-Hu Jaakko Nissi Mikko Ikävalko

JavaRMI 1 JAVA RMI. Rinnakkaisohjelmoinnin projekti 1 osa C Tekijät: Taru Itäpelto-Hu Jaakko Nissi Mikko Ikävalko JavaRMI 1 JAVA RMI Rinnakkaisohjelmoinnin projekti 1 osa C Tekijät: Taru Itäpelto-Hu Jaakko Nissi Mikko Ikävalko JavaRMI 2 Table of Contents...1 JAVA RMI...1 Yleistä...4 Arkkitehtuuri...5 Java RMI kerrosarkkitehtuuri...5

Lisätiedot

ITKP102 Ohjelmointi 1 (6 op)

ITKP102 Ohjelmointi 1 (6 op) ITKP102 Ohjelmointi 1 (6 op) Tentaattori: Antti-Jussi Lakanen 7. huhtikuuta 2017 Vastaa kaikkiin tehtäviin. Tee jokainen tehtävä erilliselle konseptiarkille. Kirjoittamasi luokat, funktiot ja aliohjelmat

Lisätiedot

13. Loogiset operaatiot 13.1

13. Loogiset operaatiot 13.1 13. Loogiset operaatiot 13.1 Sisällys Loogiset operaatiot AND, OR, XOR ja NOT. Operaatioiden ehdollisuus. Bittioperaatiot. Loogiset operaatiot ohjausrakenteissa. Loogiset operaatiot ja laskentajärjestys.

Lisätiedot

58131 Tietorakenteet ja algoritmit (syksy 2015)

58131 Tietorakenteet ja algoritmit (syksy 2015) 58131 Tietorakenteet ja algoritmit (syksy 2015) Harjoitus 2 (14. 18.9.2015) Huom. Sinun on tehtävä vähintään kaksi tehtävää, jotta voit jatkaa kurssilla. 1. Erään algoritmin suoritus vie 1 ms, kun syötteen

Lisätiedot

812341A Olio-ohjelmointi, IX Olioiden välisistä yhteyksistä

812341A Olio-ohjelmointi, IX Olioiden välisistä yhteyksistä 2016 IX Olioiden välisistä yhteyksistä Sisältö 1. Johdanto 2. Kytkentä 3. Koheesio 4. Näkyvyydestä 2 Johdanto n Ohjelmassa syntyy kytkentöjä olioiden välille Toivottuja ja epätoivottuja n Näkyvyys vaikuttaa

Lisätiedot

Sisällys. 16. Lohkot. Lohkot. Lohkot

Sisällys. 16. Lohkot. Lohkot. Lohkot Sisällys 16. ohkot Tutustutaan lohkoihin. Muuttujien ja vakioiden näkyvyys sekä elinikä erityisesti operaation lohkossa. Nimikonfliktit. Muuttujat operaation alussa vai myöhemmin? 16.1 16.2 ohkot aarisulut

Lisätiedot

Luokat ja oliot. Ville Sundberg

Luokat ja oliot. Ville Sundberg Luokat ja oliot Ville Sundberg 12.9.2007 Maailma on täynnä olioita Myös tietokoneohjelmat koostuvat olioista Σ Ο ω Μ ς υ φ Ϊ Φ Θ ψ Љ Є Ύ χ Й Mikä on olio? Tietokoneohjelman rakennuspalikka Oliolla on kaksi

Lisätiedot

Tehtävä 1. Tehtävä 2. Arvosteluperusteet Koherentti selitys Koherentti esimerkki

Tehtävä 1. Tehtävä 2. Arvosteluperusteet Koherentti selitys Koherentti esimerkki Tehtävä 1 Koherentti selitys Koherentti esimerkki ½p ½p Tehtävä 2 Täysiin pisteisiin edellytetään pelaajien tulostamista esimerkin järjestyksessä. Jos ohjelmasi tulostaa pelaajat jossain muussa järjestyksessä,

Lisätiedot

Tutoriaaliläsnäoloista

Tutoriaaliläsnäoloista Tutoriaaliläsnäoloista Tutoriaaliläsnäolokierroksella voi nyt täyttää anomuksen läsnäolon merkitsemisestä Esim. tagi ei toiminut, korvavaltimon leikkaus, yms. Hyväksyn näitä omaa harkintaa käyttäen Tarkoitus

Lisätiedot

Zeon PDF Driver Trial

Zeon PDF Driver Trial Matlab-harjoitus 2: Kuvaajien piirto, skriptit ja funktiot. Matlabohjelmoinnin perusteita Numeerinen integrointi trapezoidaalimenetelmällä voidaan tehdä komennolla trapz. Esimerkki: Vaimenevan eksponentiaalin

Lisätiedot

Listarakenne (ArrayList-luokka)

Listarakenne (ArrayList-luokka) Listarakenne (ArrayList-luokka) Mikä on lista? Listan määrittely ArrayList-luokan metodeita Listan läpikäynti Listan läpikäynti indeksin avulla Listan läpikäynti iteraattorin avulla Listaan lisääminen

Lisätiedot

HSMT Tietokannoista. Ville Leppänen. HSMT, c Ville Leppänen, IT, Turun yliopisto, 2008 p.1/32

HSMT Tietokannoista. Ville Leppänen. HSMT, c Ville Leppänen, IT, Turun yliopisto, 2008 p.1/32 HSMT Tietokannoista Ville Leppänen HSMT, c Ville Leppänen, IT, Turun yliopisto, 2008 p.1/32 Missä mennään... 1. Johdanto (1h) 2. Säikeet (2h) 3. Samanaikaisuudesta (2h) 4. Hajautetuista sovelluksista (1h)

Lisätiedot

UML -mallinnus TILAKAAVIO

UML -mallinnus TILAKAAVIO UML -mallinnus TILAKAAVIO SISÄLLYS 3. Tilakaavio 3.1 Tilakaavion alku- ja lopputilat 3.2 Tilan nimi, muuttujat ja toiminnot 3.3 Tilasiirtymä 3.4 Tilasiirtymän vai tilan toiminnot 3.5 Tilasiirtymän tapahtumat

Lisätiedot

Java-kielen perusteet

Java-kielen perusteet Java-kielen perusteet String-merkkijonoluokka 1 Ohjelmointikielten merkkijonot Merkkijonot ja niiden käsittely on välttämätöntä ohjelmoinnissa Valitettavasti ohjelmointikielten tekijät eivät tätä ole ottaneet

Lisätiedot