Skip to content

Asiantuntijalta: Java 21

09/11/2023
Jaa

Trivoren asiantuntija Stefan Roos esittelee syyskuussa 2023 julkaistun Java-ohjelmointikielen Long Term Support -version mukanaan tuomia uusia ominaisuuksia. Näihin lukeutuvat muun muassa virtuaalisäikeet, parannukset record-tietueen käsittelyyn ehtolauseissa instanceof-avainsanan kanssa sekä parannukset switch-lauseeseen.


Kirjoittajasta

Stefan Roos toimii Trivoren Lead Developerina. Työssään hän toimii erityisesti integraatioiden ja Trivoren IAM-järjestelmän parissa.

 


Java-ohjelmointikielen uusin LTS-versio julkaistiin tuotantovalmiina (General Availability) 19.9.2023. LTS tarkoittaa versiota, jota tuetaan pitkään (Long Term Support). LTS-versioita julkaistaan kahden vuoden välein. Yleisesti pidetään hyvänä käytäntönä käyttää LTS-versiota javasta ohjelmistokehityksessä. Nyt julkaistussa versiossa on 15 uutta ominaisuutta. Esittelen nämä ominaisuudet lyhyesti tässä muutaman esimerkin ja oman havainnon kanssa. Jokaisesta näistä ominaisuuksista saisi varmastikin oman pitkän kirjoituksen aikaiseksi.

Kiinnostavimmat uusista ominaisuuksista mielestäni ovat virtuaalisäikeet (Virtual Threads, JEP-444), parannukset record-tietueen käsittelyyn ehtolauseissa instanceof-avainsanan kanssa (JEP-440) sekä parannukset switch-lausekkeeseen (JEP-441). ZGC-roskienkerääjä kehittyi myös (JEP-439), mikä toivottavasti tuo hieman lisää tehokkuutta joissain tilantiessa. Tämän lisäksi Javaan saatiin KEM-rajapinta (JEP-452), eli kryptografiassa avainten turvaamiseen käytettävä rajapinta.

 

Virtuaalisäikeet

Javassa rinnakkaisuuden perusyksikkönä toimii säie (thread). Kun halutaan, että asioita tapahtuu samanaikaisesti, luodaan uusi säie jokaiselle rinnakkaisesti tapahtuvalle asialle. Tämä on ennen vaatinut käyttöjärjestelmäsäikeen jokaista java-säiettä kohti. Käyttöjärjestelmäsäikeen luominen vie resursseja, jonka lisäksi säikeiden maksimimäärä voi joissain tilanteissa tulla vastaan ennen kuin suorituskyky varsinaisesti loppuu. Tätä ongelmaa on ratkottu asynkronisella ohjelmoinnilla, jonka avulla ohjelman suorituksen eri osia suoritetaan eri säikeissä sen mukaan, miten ne kulloinkin valmistuvat. Asynkroninen ohjelmointi tuo kuitenkin omat haasteensa etenkin debuggauksen sekä luettavuuden kannalta. Virtuaalisäikeet ratkaisevat tämän ongelman.

Virtuaalisäikeiden avulla voidaan rinnakkaisuutta luoda sovellukseen vastaavasti kuin aiemminkin säikeiden avulla, mutta niillä ei ole vastaavia rajoituksia kuin normaaleilla käyttöjärjestelmäsäikeillä. Virtuaalisäikeiden luonti on kevyttä ja niitä voidaan luoda hyvin paljon. Käytännössä virtuaalisäikeet ovat java-kielen oma toteutus säikeistä ja ne toimivat hieman samalla periaatteella kuin virtuaalimuisti käyttöjärjestelmässä. Virtuaalimuisti toimii siten, että tarjotaan järjestelmän käyttöön enemmän muistia kuin varsinaista RAM-muistia on tarjolla. Osa järjestelmälle tarjottavasta muistista voidaan allokoida esimerkiksi kiintolevyltä. Vastaavasti Javassa voidaan jatkossa luoda suuri määrä virtuaalisäikeitä, jotka oikeasti hyödyntävät käyttöjärjestelmässä pienempää määrää käyttöjärjestelmäsäikeitä. Virtuaalisäikeitä voi ajatella abstraktiona normaalien käyttöjärjestelmäsäikeiden päällä. Tämän abstraktion kautta säikeiden käyttäminen toteutetaan resurssitehokkaasti.

 

Record-tietueen parannukset

Uutena ominaisuutena saadaan kyky purkaa ehtolauseen sisällä Record-tietue osiksi, jolloin voidaan suoraan sen osiin viitata ehtolauseen sisällä. Tämä toimii tarkemmin niissä tilanteissa, kun tarkastellaan, mitä tyyppiä käsitelty muuttuja on, eli instanceof – tarkistuslauseissa. Esimerkki tällaisesta on seuraavassa koodilistauksessa.

 

Tämän uuden ominaisuuden avulla voimme välttää muutaman rivin kirjoittamisen ja suoraan käyttää ehtolauseessa luotuja uusia muuttujia, jotka osoittavat tietueen sisällä oleviin osiin. Tämä toimii myös hyvin sisäkkäisille (nested) tietueille.

 

Switch-rakenne

Switch-lause pystyy jatkossa tutkimaan kuvioita (eng. pattern) (JEP-441). Tämä tarkoittaa sitä, että jatkossa switch-lauseessa voidaan tarkastella esimerkiksi objektin tyyppiä instanceof -tyylisesti. Jatkossa myös null on hyväksytty tapaus switch-rakenteessa, mikäli se erikseen määritellään omaksi tapauksekseen, mikäli sitä ei ole määritelty, aiheuttaa koodi tutun NullPointerException-poikkeuksen. Voimme myös jatkossa lisätä tapausten alle tarkentavia ehtoja when-lauseella. Tämä tekee koodista luettavamman ja voimme välttää ifelse-rakenteet tapausten sisällä. Kohtalaisen kattava esimerkki uudenlaisista ominaisuuksista switch-rakenteessa on seuraavassa koodilistauksessa.

 

Roskienkeruu

Java-sovellusten suorituskykyä saadaan parannettua roskienkeruun osalta siten, että ZGC-roskienkerääjä alkaa pitämään kirjaa erikseen nuorista sekä vanhoista objekteista sovelluksen muistissa. Tämän avulla voidaan nuorempia objekteja käydä läpi roskienkerääjällä useammin. Objekteilla on suuri todennäköisyys kuolla nuorina, vanhoja objekteja ei ole tarpeen kerätä niin usein. (JEP-439)

 

Kryptografia

Suurin parannus Java 21 -versiossa kryptografiaan liittyen on varmasti tuki KEM:lle (JEP452). KEM tarkoittaa avaimen kapselointimekanismeja (eng. Key Encapsulation Mechanism). Tämä ominaisuus tuo Javaan rajapinnat KEM-menetelmien käyttämistä varten. Rajapintojen avulla voidaan luoda avainpari, kapseloida avain ja purkaa avaimen kapselointi.

KEMin avulla voidaan julkisesta avaimesta johtaa siihen liittyvä symmetrinen avain eikä näin tarvita lainkaan täyttöä (eng. padding) toisin kuin perinteisessä menetelmässä. KEM on moderni salaustekniikka, joka on pohjana hybridissä julkisen avaimen salausmekanismissa. KEMien arvioidaan toimivan pohjana seuraavan sukupolven salausmekanismien pohjalla.

 

Muut pienemmät muutokset

Tähän Java-versioon sisältyy myös muutama pieni muutos, joiden vaikutus on melko vähäinen. Nämä muutokset eivät käytännössä vaikuta toimintaan vielä mitenkään. Muutokset ovat seuraavat:

  • JDK valmistautuu estämään dynaamisten agenttien lataamisen (JEP-451). Valmistautuminen tässä kohtaa käytännössä tarkoittaa sitä, että JDK esittää varoituksen silloin, mikäli tällaista tapahtuu.
  • 32-bittisen Windows-käyttöjärjestelmän JDK-version koostaminen (build) aiotaan jatkossa lopettaa (JEP-449). Tähän valmistaudutaan deprekoimalla tämän tyyppinen buildi. Deprekointi tässä kohtaa myös käytännössä tarkoittaa sitä, että jos tällainen build-konfiguraatio nyt tehdään, tulee siitä varoitus. Myöhemmin tuki tälle käyttöjärjestelmälle poistetaan kokonaan.

 

Esikatseluominaisuudet

Java-versioissa on usein esikatseluominaisuuksia. Nämä ovat ominaisuuksia, jotka ovat käytännössä valmiita, mutta saattavat vielä muuttua. On myös mahdollista, että niitä ei sisällytetäkään kieleen mukaan lainkaan. Esikatseluominaisuudet pitää kytkeä erikseen päälle, jotta niitä voidaan käyttää. Niitä parannetaan ja jatkokehitetään java-kehittäjien palautteen perusteella. Tässä uudessa versiossa on seuraavat esikatseltavat ominaisuudet:

  • String templates (JEP-430)
  • Foreign Function & Memory API (JEP-442)
  • Unnamed Patterns and Variables (JEP-443)
  • Unnamed Classes and Instance Main Methods (JEP-445)
  • Scoped Values (JEP-446)
  • Structured Concurrency (JEP-453)

Näistä esikatseltavista ominaisuuksista mielestäni hyvin kiinnostava on etenkin String Templates, eli merkkijonomallit. Tämä tuo helpotusta java-koodaajan päivittäiseen työhön. Seuraavassa esimerkissä on pieni malli, miten tätä esikatseltavana olevaa ominaisuutta voidaan käyttää.

 

 

Koodiesimerkin toisella rivillä siis käytetään STR-templateprosessoria, jonka avulla ajonaikaisesti käsitellään templaten sisältö. STR on public static final -tyyppinen ja on automaattisesti sisällytetty (import) kaikkiin java-tiedostoihin; sitä ei siis erikseen tarvitse importata. Viimeinen rivi koodilistauksessa varmistaa, että tämän templatoinnin tuloksena saatiin String, jossa name-muuttuja on korvattu muuttujan varsinaisella arvolla.

 

JEP-442, Foreign Function & Memory API, eli FFM API, tuo parannuksia natiiviresurssien käyttöön. Tämä tarkoittaa mm. natiivien kirjastojen funktiokutsuja esimerkiksi käyttöjärjestelmässä tai järjestelmän muistialueen käsittelyä. Käytännössä tässä on tarkoituksena korvata aiempi Java Native Interface (JNI) helppokäyttöisemmällä ja tehokkaammalla rajapinnalla.

JEP-443 tuo mahdollisuuden käyttää ehtolauseissa nimeämättömiä record-malleja instanceOf-tyylisissä ehdoissa. Käytännössä siis voidaan tehdä jatkossa niin, että instanceOf-vertailuissa ja switch-tapauksissa määritellään vain merkitsevä osa recordrakennetta. Eli siis se osa rakenteesta, jota oikeasti tullaan käyttämään. Tämän muutoksen avulla saadaan siis huomattavasti siistimpää koodia aikaiseksi eikä tuoda turhia muuttujia ehtolauseisiin, joita ei sitten kuitenkaan tarvittaisi.

Toinen tässä tarkasteltavana oleva ominaisuus on nimettömät muuttujat. Tämä käytännössä tarkoittaa sitä, että voimme olla huomioimatta muuttujia, jotka kuitenkin ovat pakollisia joissain rakenteissa. Eli esimerkiksi for each -loopissa, jossa on pakko määritellä muuttujan nimi, voimme jättää sen pois asettamalla sen kohdalle alaviivan. Eli korvaamalla muuttujanimen alaviivalla, jätämme pakollisen muuttujan huomiotta eikä se tällöin varaa muuttujanimeä turhaan koodissa.

JEP-445 taas tuo esikatseltavaksi nimettömät luokat sekä instantioidun Main-metodin. Tämä käytännössä tarkoittaa sitä, että yksinkertaisen hello world -tyylisen sovelluksen voisi jatkossa kirjoittaa seuraavan listauksen mukaisesti.

Emme siis välttämättä jatkossa enää tarvitse luokkaa tällaisen triviaalin sovelluksen ympärille, vaan se luodaan nimettömänä luokkana automaattisesti. Lisäksi Main-metodin ei tarvitse olla staattinen metodi enää eikä sen tarvitse ottaa parametrina merkkijonolistaa, mikäli sitä ei tarvitse sovelluksessa. Näiden muutosten on tarkoitus tuoda Java lähestyttävämmäksi aloittelijoille.

JEP-446 tuo kieleen esikatseltavana ominaisuutena ScopedValue -tyyppiset muuttujat. Nämä helpottavat turvallista tiedon jakamista sovelluksessa vastaavaan tapaan kuten ThreadLocal. Eli ScopedValue-muuttujalla on jokaista säiettä kohdin eri arvo, ja tämä pätee myös uusiin virtuaalisäikeisiin. Mutta toisin kuin ThreadLocaleilla, ScopedValue kirjoitetaan vain kertaalleen, jonka lisäksi sillä on rajoitettu elinaika. Tämä tekee ScopedValueista turvallisemman käyttää kuin ThreadLocal.

JEP-453 esittelee strukturoidun rinnakkaisuuden. Kyse on käytännössä paremman kontrollin rakentaminen eri threadeissa tapahtuvien asioiden hallintaan. Tyypillisesti tilanne on se, että tehtävillä, joita suoritetaan rinnakkain, on riippuvuus toisiinsa tavalla tai toisella. Tämän muutoksen avulla pystytään kunnolla luomaan suhde näiden tehtävien välillä. Jos jompi kumpi tehtävä epäonnistuu, peruutetaan toisen tehtävän suoritus myös, ellei se ole jo valmistunut. Lisäksi, jos tehtävät laukaiseva thread peruutetaan, peruuttaa se kaikki tehtäviin allokoidut threadit.

 

Lopuksi

Tämän tekstin kirjoittaminen oli huomattavasti työläämpi projekti kuin alunperin ajattelin. Muutin tekstin rakennetta pariin otteeseen, jonka lisäksi jouduin jonkin verran opiskelemaan uusia asioita.

Kiitokset vielä kollegoilleni alustavan tekstin tarkastamisesta ja palautteesta siihen. Erityisesti kiitokset Sebastianille ja Asserille erinomaisista huomioista ja korjausehdotuksista.

 


Lähteet

Tässä kirjoituksessa esitetyt tiedot perustuvat pääosin OpenJDK:n julkaisemiin tietoihin.

Jaa