Ing pawulangan iki, kita bakal ngomong umum babagan nggarap kelas java.lang.ThreadLocal<> lan carane nggunakake ing lingkungan multithreaded.

Kelas ThreadLocal digunakake kanggo nyimpen variabel . A fitur khusu saka kelas iki sing nyimpen salinan sawijining kapisah saka nilai kanggo saben thread nggunakake.

Delving luwih jero menyang operasi saka kelas, kita bisa mbayangno Peta sing peta Utas kanggo nilai, saka thread saiki njupuk Nilai cocok nalika perlu kanggo nggunakake.

Konstruktor kelas ThreadLocal

Konstruktor Tumindak
ThreadLocal() Nggawe variabel kosong ing Jawa

Metode

Metode Tumindak
entuk() Ngasilake nilai variabel lokal utas saiki
set() Nyetel nilai variabel lokal kanggo thread saiki
mbusak () Mbusak nilai variabel lokal saka utas saiki
ThreadLocal.withInitial() Cara pabrik tambahan sing nyetel nilai awal

entuk() & set()

Ayo nulis conto ing ngendi kita nggawe rong counter. Pisanan, variabel biasa, bakal kanggo ngetung jumlah benang. Kapindho kita bakal mbungkus ing ThreadLocal . Lan kita bakal weruh carane padha bisa bebarengan. Pisanan, ayo nulis kelas ThreadDemo sing entuk warisan Runnable lan ngemot data lan metode run () sing paling penting . Kita uga bakal nambah cara kanggo nampilake counter ing layar:


class ThreadDemo implements Runnable {

    int counter;
    ThreadLocal<Integer> threadLocalCounter = new ThreadLocal<>();

    public void run() {
        counter++;

        if(threadLocalCounter.get() != null) {
            threadLocalCounter.set(threadLocalCounter.get() + 1);
        } else {
            threadLocalCounter.set(0);
        }
        printCounters();
    }

    public void printCounters(){
        System.out.println("Counter: " + counter);
        System.out.println("threadLocalCounter: " + threadLocalCounter.get());
    }
}

Kanthi saben roto saka kelas kita, kita nambahnglawanvariabel nelpon cara njaluk () kanggo njupuk data saka variabel ThreadLocal . Yen thread anyar ora duwe data, banjur kita bakal nyetel menyang 0. Yen ana data, kita bakal nambah siji. Lan ayo nulis metode utama :


public static void main(String[] args) {
    ThreadDemo threadDemo = new ThreadDemo();

    Thread t1 = new Thread(threadDemo);
    Thread t2 = new Thread(threadDemo);
    Thread t3 = new Thread(threadDemo);

    t1.start();
    t2.start();
    t3.start();

}

Mlaku kelas kita, kita weruh sing variabel ThreadLocal tetep padha preduli saka thread sing ngakses, nanging nomer Utas mundak akeh.

Counter: 1
Counter: 2
Counter: 3
threadLocalCounter: 0
threadLocalCounter: 0
threadLocalCounter: 0

Proses rampung nganggo kode metu 0

mbusak ()

Kanggo mangerteni carane cara mbusak , kita mung bakal ngganti kode ing kelas ThreadDemo :


if(threadLocalCounter.get() != null) {
      threadLocalCounter.set(threadLocalCounter.get() + 1);
  } else {
      if (counter % 2 == 0) {
          threadLocalCounter.remove();
      } else {
          threadLocalCounter.set(0);
      }
  }

Ing kode iki, yen thread counter iku nomer malah, banjur kita bakal nelpon mbusak () cara ing variabel ThreadLocal . asil:

Counter: 3
threadLocalCounter: 0
Counter: 2
threadLocalCounter: null
Counter: 1
threadLocalCounter: 0

Proses rampung nganggo kode metu 0

Lan ing kene kita bisa ndeleng manawa variabel ThreadLocal ing utas kapindho null .

ThreadLocal.withInitial()

Cara iki nggawe variabel thread-lokal.

Implementasi kelas ThreadDemo :


class ThreadDemo implements Runnable {

    int counter;
    ThreadLocal<Integer> threadLocalCounter = ThreadLocal.withInitial(() -> 1);

    public void run() {
        counter++;
        printCounters();
    }

    public void printCounters(){
        System.out.println("Counter: " + counter);
        System.out.println("threadLocalCounter: " + threadLocalCounter.get());
    }
}

Lan kita bisa ndeleng asil kode kita:

Counter: 1
Counter: 2
Counter: 3
threadLocalCounter: 1
threadLocalCounter: 1
threadLocalCounter: 1

Proses rampung nganggo kode metu 0

Napa kita kudu nggunakake variabel kasebut?

ThreadLocal menehi abstraksi liwat variabel lokal ing hubungan kanggo thread eksekusi java.lang.Thread .

Variabel ThreadLocal beda karo sing biasa amarga saben thread duwe conto variabel sing diinisialisasi dhewe, sing diakses liwat metode get () lan set () .

Saben utas, kayata conto kelas Utas , duwe peta variabel ThreadLocal sing ana gandhengane. Tombol peta minangka referensi kanggo obyek ThreadLocal , lan nilai kasebut minangka referensi kanggo variabel ThreadLocal "angsal" .

Apa kelas Random ora cocog kanggo ngasilake nomer acak ing aplikasi multithreaded?

Kita nggunakake kelas Random kanggo njaluk nomer acak. Nanging apa bisa uga ing lingkungan multithreaded? Bener, ora. Acak ora cocok kanggo lingkungan multithreaded, amarga nalika sawetara Utas ngakses kelas ing wektu sing padha, kinerja nandhang sangsara.

Kanggo ngatasi masalah iki, JDK 7 ngenalaken kelas java.util.concurrent.ThreadLocalRandom kanggo generate nomer acak ing lingkungan multithreaded. Iku kasusun saka rong kelas: ThreadLocal lan Random .

Nomer acak sing ditampa dening siji utas ora gumantung saka benang liyane, nanging java.util.Random nyedhiyakake nomer acak global. Uga, ora kaya Random , ThreadLocalRandom ora ndhukung seeding eksplisit. Nanging, overrides cara setSeed () dipun warisaken saka Random , supaya tansah unsupportedOperationException nalika disebut.

Ayo goleki metode kelas ThreadLocalRandom :

Metode Tumindak
ThreadLocalRandom saiki() Ngasilake ThreadLocalRandom saka thread saiki.
int sabanjuré (int bit) Ngasilake nomer pseudo-acak sabanjuré.
dobel sabanjuréDouble(dobel paling, dobel bound) Ngasilake nomer pseudorandom saka distribusi seragam antarane paling (inklusif) lan kaiket (eksklusif).
int nextInt(int least, int bound) Ngasilake nomer pseudorandom saka distribusi seragam antarane paling (inklusif) lan kaiket (eksklusif).
long nextLong (long n) Ngasilake nomer pseudorandom saka distribusi seragam antarane 0 (inklusif) lan nilai sing ditemtokake (eksklusif).
long nextLong (paling dawa, dawa terikat) Ngasilake nomer pseudorandom saka distribusi seragam antarane paling (inklusif) lan kaiket (eksklusif).
void setSeed(biji panjang) Mbuwang UnsupportedOperationException . Generator iki ora ndhukung seeding.

Entuk nomer acak nggunakake ThreadLocalRandom.current()

ThreadLocalRandom minangka kombinasi saka kelas ThreadLocal lan Random . Iki entuk kinerja sing luwih apik ing lingkungan multithreaded kanthi mung ngindhari akses bebarengan menyang kelas Random .

Ayo ngleksanakake conto sing nglibatake pirang-pirang utas lan deleng aplikasi kita karo kelas ThreadLocalRandom :


import java.util.concurrent.ThreadLocalRandom;

class RandomNumbers extends Thread {

    public void run() {
        try {
            int bound = 100;
            int result = ThreadLocalRandom.current().nextInt(bound);
            System.out.println("Thread " + Thread.currentThread().getId() + " generated " + result);
        }
        catch (Exception e) {
            System.out.println("Exception");
        }
    }

    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();

				for (int i = 0; i < 10; i++) {
            RandomNumbers randomNumbers = new RandomNumbers();
            randomNumbers.start();
        }

        long endTime = System.currentTimeMillis();

        System.out.println("Time taken: " + (endTime - startTime));
    }
}

Hasil program kami:

Wektu dijupuk: 1
Utas 17 kui 13
Utas 18 kui 41
Utas 16 kui 99
Utas 19 kui
25 Utas 23 kui 33 Utas 24 kui
21
Utas 15 kui 15
Utas 21 kui 28 Utas
22 kui 3 Utas digawe 97
Utas

Lan saiki ayo ngganti kelas RandomNumbers lan gunakake Random ing:


int result = new Random().nextInt(bound);
Wektu dijupuk: 5
Utas 20 kui 48
Utas 19 kui 57
Utas 18 kui 90
Utas 22 kui 43 Utas 24
kui 7
Utas 23 kui 63
Utas 15 kui 2
Utas 16 kui 40
Utas 17 kui 29
Utas 121

Cathetan! Ing tes kita, kadhangkala asile padha lan kadhangkala beda. Nanging yen kita nggunakake luwih akeh utas (ujare, 100), asil bakal katon kaya iki:

Random — 19-25 ms
ThreadLocalRandom — 17-19 ms

Mulane, luwih akeh utas ing aplikasi kita, luwih gedhe kinerja nalika nggunakake kelas Random ing lingkungan multithreaded.

Kanggo nyimpulake lan mbaleni maneh beda antarane kelas Random lan ThreadLocalRandom :

Acak ThreadLocalRandom
Yen thread beda nggunakake conto padha Random , bakal ana konflik lan kinerja bakal nandhang sangsara. Ora ana konflik utawa masalah, amarga nomer acak kui lokal kanggo thread saiki.
Nggunakake rumus congruential linear kanggo ngganti nilai awal. Generator nomer acak diwiwiti kanthi nggunakake wiji sing digawe sacara internal.
Migunani ing aplikasi ngendi saben thread nggunakake pesawat dhewe saka obyek Acak . Migunani ing aplikasi ngendi sawetara Utas nggunakake nomer acak ing podo karo ing pools Utas.
Iki kelas wong tuwa. Iki kelas bocah.