Vodič za sunce.misc.Nesigurno

1. Pregled

U ovom ćemo članku pogledati fascinantnu nastavu koju pruža JRE - Nesigurno od sunce.razno paket. Ova klasa pruža nam mehanizme niske razine koji su dizajnirani da ih koristi samo osnovna Java knjižnica, a ne i standardni korisnici.

To nam pruža mehanizme niske razine prvenstveno dizajnirane za internu upotrebu unutar temeljnih knjižnica.

2. Dobivanje instance Nesigurno

Prvo, da biste mogli koristiti Nesigurno klase, moramo dobiti instancu - što nije jednostavno s obzirom da je klasa dizajnirana samo za internu upotrebu.

Način dobivanja instance je putem statičke metode getUnsafe (). Upozorenje je da će prema zadanim postavkama ovo baciti znak SecurityException.

Srećom, primjerak možemo dobiti pomoću refleksije:

Polje f = Unsafe.class.getDeclaredField ("theSigurno"); f.setAccessible (true); nesigurno = (nesigurno) f.get (null);

3. Instanciranje predavanja pomoću Nesigurno

Recimo da imamo jednostavnu klasu s konstruktorom koji postavlja vrijednost varijable kada se objekt kreira:

class InitializationOrdering {private long a; public InitializationOrdering () {this.a = 1; } public long getA () {return this.a; }}

Kada inicijaliziramo taj objekt pomoću konstruktora, dobiti() metoda će vratiti vrijednost 1:

InitializationOrdering o1 = novo InitializationOrdering (); assertEquals (o1.getA (), 1);

Ali možemo koristiti allocateInstance () metoda pomoću Nesigurno. Dodijelit će samo memoriju za našu klasu i neće pozvati konstruktor:

InitializationOrdering o3 = (InitializationOrdering) unsafe.allocateInstance (InitializationOrdering.class); assertEquals (o3.getA (), 0);

Primijetite da konstruktor nije pozvan i zbog te činjenice dobiti() metoda vratila zadanu vrijednost za dugo vrsta - koja je 0.

4. Promjena privatnih polja

Recimo da imamo razred koji drži a tajna privatna vrijednost:

klasa SecretHolder {private int SECRET_VALUE = 0; javna logička tajnaIsDisclosed () {return SECRET_VALUE == 1; }}

Koristiti putInt () metoda iz Nesigurno, možemo promijeniti vrijednost privatnog SECRET_VALUE polje, mijenjanje / kvarenje stanja te instance:

SecretHolder secretHolder = novi SecretHolder (); Polje f = secretHolder.getClass (). GetDeclaredField ("SECRET_VALUE"); unsafe.putInt (secretHolder, unsafe.objectFieldOffset (f), 1); assertTrue (secretHolder.secretIsDisclosed ());

Jednom kad dobijemo polje pozivom refleksije, možemo promijeniti njegovu vrijednost bilo kojoj drugoj int vrijednost pomoću Nesigurno.

5. Bacanje iznimke

Kôd koji se poziva putem Nesigurno se ne preispituje na isti način od strane kompajlera kao i redoviti Java kôd. Možemo koristiti throwException () metoda za bacanje bilo koje iznimke bez ograničavanja pozivatelja da obrađuje tu iznimku, čak i ako je provjerena iznimka:

@Test (očekuje se = IOException.class) javna praznina givenUnsafeThrowException_whenThrowCheckedException_thenNotNeedToCatchIt () {unsafe.throwException (novi IOException ()); }

Nakon bacanja IOException, što je provjereno, ne trebamo ga uhvatiti niti navesti u deklaraciji metode.

6. Memorija izvan gomile

Ako u aplikaciji ponestane dostupne memorije na JVM-u, mogli bismo na kraju prisiliti GC postupak na prečesto pokretanje. Idealno bi bilo da želimo posebno područje memorije, izvan gomile i koje ne kontrolira GC postupak.

The allocateMemory () metoda iz Nesigurno klasa daje nam sposobnost da rasporedimo ogromne predmete s hrpe, što znači da ovu memoriju neće vidjeti i uzeti u obzir GC i JVM.

To može biti vrlo korisno, ali moramo imati na umu da se ovom memorijom mora ručno upravljati i ispravno je povratiti freeMemory () kad više nije potreban.

Recimo da želimo stvoriti veliki niz bajtova izvan memorije. Možemo koristiti allocateMemory () metoda da se to postigne:

klasa OffHeapArray {privatni konačni statički int BYTE = 1; privatna duga veličina; privatna duga adresa; javni OffHeapArray (duga veličina) baca NoSuchFieldException, IllegalAccessException {this.size = size; adresa = getUnsafe (). allocateMemory (veličina * BYTE); } private Unsafe getUnsafe () baca IllegalAccessException, NoSuchFieldException {Polje f = Unsafe.class.getDeclaredField ("theUnsafe"); f.setAccessible (true); return (nesigurno) f.get (null); } javni void set (long i, byte value) baca NoSuchFieldException, IllegalAccessException {getUnsafe (). putByte (adresa + i * BYTE, vrijednost); } public int get (long idx) baca NoSuchFieldException, IllegalAccessException {return getUnsafe (). getByte (adresa + idx * BYTE); } javna duga veličina () {veličina povrata; } javna praznina freeMemory () baca NoSuchFieldException, IllegalAccessException {getUnsafe (). freeMemory (adresa); }
}

U konstruktoru datoteke OffHeapArray, inicijaliziramo niz koji je od datog veličina. Pohranjujemo početnu adresu niza u adresa polje. The postavi () metoda uzima indeks i dano vrijednost koji će biti pohranjeni u nizu. The dobiti() metoda dohvaća vrijednost bajta koristeći njegov indeks koji je odmak od početne adrese niza.

Dalje, možemo rasporediti taj niz od heap-a koristeći njegov konstruktor:

long SUPER_SIZE = (long) Integer.MAX_VALUE * 2; Niz OffHeapArray = novi OffHeapArray (SUPER_SIZE);

U ovaj niz možemo staviti N brojeva bajt vrijednosti, a zatim dohvatiti te vrijednosti, sažimajući ih kako bismo provjerili radi li pravilno adresiranje:

int zbroj = 0; for (int i = 0; i <100; i ++) {array.set ((long) Integer.MAX_VALUE + i, (byte) 3); zbroj + = niz.get ((dugo) Integer.MAX_VALUE + i); } assertEquals (array.size (), SUPER_SIZE); assertEquals (zbroj, 300);

Na kraju, trebamo vratiti memoriju natrag u OS pozivom freeMemory ().

7. CompareAndSwap Operacija

Vrlo učinkoviti konstrukti iz java.konkurentan paket, poput AtomicInteger, koriste compareAndSwap () metode iz Nesigurno ispod, kako bi se pružile najbolje moguće izvedbe. Ova se konstrukcija široko koristi u algoritmima bez zaključavanja koji mogu koristiti instrukcije CAS procesora kako bi pružili veliku brzinu u usporedbi sa standardnim pesimističkim mehanizmom sinkronizacije u Javi.

Možemo konstruirati brojač zasnovan na CAS-u koristeći compareAndSwapLong () metoda iz Nesigurno:

klasa CASCounter {privatno Nesigurno nesigurno; privatni hlapljivi dugi brojač = 0; privatni dugi ofset; private Unsafe getUnsafe () baca IllegalAccessException, NoSuchFieldException {Polje f = Unsafe.class.getDeclaredField ("theUnsafe"); f.setAccessible (true); return (nesigurno) f.get (null); } javni CASCounter () baca izuzetak {nesigurno = getUnsafe (); offset = unsafe.objectFieldOffset (CASCounter.class.getDeclaredField ("brojač")); } prirast javne praznine () {mnogo prije = brojač; while (! unsafe.compareAndSwapLong (this, offset, before, before + 1)) {before = counter; }} public long getCounter () {return brojač; }}

U CASCounter konstruktor dobivamo adresu polja brojača kako bismo je mogli koristiti kasnije u prirast () metoda. To polje treba deklarirati kao hlapljivo, kako bi bilo vidljivo svim nitima koje pišu i čitaju ovu vrijednost. Koristimo objectFieldOffset () metoda za dobivanje memorijske adrese ofset polje.

Najvažniji dio ove klase je prirast () metoda. Koristimo compareAndSwapLong () u dok petlja za povećanje prethodno preuzete vrijednosti, provjeravajući je li se ta prethodna vrijednost promijenila otkad smo je dohvatili.

Ako je i uspjelo, tada ponavljamo tu operaciju dok ne uspijemo. Ovdje nema blokiranja, zbog čega se ovo naziva algoritam bez zaključavanja.

Naš kôd možemo testirati povećavanjem dijeljenog brojača iz više niti:

int NUM_OF_THREADS = 1_000; int NUM_OF_INCREMENTS = 10_000; Usluga ExecutorService = Executors.newFixedThreadPool (NUM_OF_THREADS); CASCounter casCounter = novi CASCounter (); IntStream.rangeClosed (0, NUM_OF_THREADS - 1) .forEach (i -> service.submit (() -> IntStream .rangeClosed (0, NUM_OF_INCREMENTS - 1) .forEach (j -> casCounter.increment ())));

Dalje, da bismo ustvrdili da je stanje brojača ispravno, iz njega možemo dobiti vrijednost brojača:

assertEquals (NUM_OF_INCREMENTS * NUM_OF_THREADS, casCounter.getCounter ());

8. Parkiranje / odvajanje

Postoje dvije fascinantne metode u Nesigurno API koje JVM koristi za prebacivanje niti konteksta. Kada nit čeka neku akciju, JVM može ovu nit blokirati pomoću park() metoda iz Nesigurno razred.

Vrlo je sličan Object.wait () metoda, ali poziva izvorni OS kôd, iskorištavajući tako neke arhitektonske specifičnosti za postizanje najboljih performansi.

Kad je nit blokiran i treba ga ponovno pokrenuti, JVM koristi odspojiti () metoda. Te ćemo pozive metoda često vidjeti na odlagalištima niti, posebno u aplikacijama koje koriste spremišta niti.

9. Zaključak

U ovom smo članku gledali Nesigurno klase i njezini najkorisniji konstrukti.

Vidjeli smo kako pristupiti privatnim poljima, kako rasporediti memoriju izvan hrpe i kako koristiti konstrukciju usporedi i zamijeni za implementaciju algoritama bez zaključavanja.

Provedbu svih ovih primjera i isječaka koda možete pronaći na GitHubu - ovo je Maven projekt, pa bi ga trebalo lako uvesti i pokrenuti kakav jest.


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