Mengapa variabel Java ThreadLocal menjadi statis

101

Saya membaca JavaDoc untuk Threadlocal di sini

https://docs.oracle.com/javase/1.5.0/docs/api/java/lang/ThreadLocal.html

dan dikatakan "Instance ThreadLocal biasanya adalah bidang statis pribadi di kelas yang ingin mengaitkan status dengan utas (misalnya, ID pengguna atau ID Transaksi)."

Tetapi pertanyaan saya adalah mengapa mereka memilih untuk membuatnya statis (biasanya) - membuat hal-hal agak membingungkan untuk memiliki status "per utas" tetapi bidangnya statis?

kellyfj
sumber

Jawaban:

131

Karena jika itu adalah bidang level instance, maka sebenarnya akan menjadi "Per Thread - Per Instance", bukan hanya "Per Thread" yang dijamin. Biasanya itu bukan semantik yang Anda cari.

Biasanya memegang sesuatu seperti objek yang dicakup ke Percakapan Pengguna, Permintaan Web, dll. Anda tidak ingin mereka juga di-sub-cakupan ke instance kelas.
Satu permintaan web => satu sesi Ketekunan.
Bukan satu permintaan web => satu sesi ketekunan per objek.

Affe
sumber
2
Saya suka penjelasan ini karena menunjukkan bagaimana ThreadLocal akan digunakan
kellyfj
4
Per-thread-per-instance dapat menjadi semantik yang berguna, tetapi sebagian besar penggunaan untuk pola itu akan melibatkan begitu banyak objek sehingga akan lebih baik jika menggunakan a ThreadLocaluntuk menyimpan referensi ke hash-set yang memetakan objek ke instance per-thread.
supercat
@optional Ini hanya berarti setiap instance dari non-statis ThreadLocalakan menyimpan data lokal-threadnya sendiri meskipun ThreadLocalinstance tersebut ada di thread yang sama. Tidak selalu salah untuk melakukan itu - saya kira itu mungkin pola yang paling tidak populer dari keduanya
geg
17

Buatlah menjadi statis atau jika Anda mencoba untuk menghindari bidang statis apa pun di kelas Anda - jadikan kelas itu sendiri tunggal dan kemudian Anda dapat dengan aman menggunakan tingkat instance ThreadLocal selama Anda memiliki tunggal itu tersedia secara global.

Adnan Memon
sumber
12

Tidak harus seperti itu. Yang penting adalah itu harus tunggal.

tak ternilai
sumber
3

Alasannya adalah variabel diakses melalui penunjuk yang terkait dengan utas. Mereka bertindak seperti variabel global dengan cakupan utas, oleh karena itu statis adalah yang paling cocok. Ini adalah cara Anda mendapatkan status lokal utas dalam hal-hal seperti pthreads jadi ini mungkin hanya kecelakaan sejarah dan implementasi.

Ukko
sumber
1

Penggunaan threadlocal pada instance per utas adalah jika Anda ingin sesuatu terlihat di semua metode objek dan membuatnya aman untuk utas tanpa menyinkronkan akses ke sana seperti yang Anda inginkan untuk bidang biasa.

Chris Mawata
sumber
1

Lihat ini , ini memberi pemahaman yang lebih baik.

Singkatnya, ThreadLocalobjek bekerja seperti peta nilai kunci. Saat ThreadLocal get/setmetode pemanggilan thread , ia akan mengambil / menyimpan objek thread di kunci peta, dan nilai dalam nilai peta. Itulah mengapa utas yang berbeda memiliki salinan nilai yang berbeda (yang ingin Anda simpan secara lokal), karena utas tersebut berada di entri peta yang berbeda.

Itulah mengapa Anda hanya membutuhkan satu peta untuk menyimpan semua nilai. Meskipun tidak perlu, Anda dapat memiliki beberapa peta (tanpa mendeklarasikan statis) untuk menyimpan setiap objek utas juga, yang, ini benar-benar berlebihan, itulah mengapa variabel statis lebih disukai.

GMsoF
sumber
-1

static final ThreadLocal variabel aman untuk benang.

staticmembuat variabel ThreadLocal tersedia di beberapa kelas untuk thread masing-masing saja. Ini semacam deklarasi variabel global dari variabel lokal thread masing-masing di beberapa kelas.

Kami dapat memeriksa keamanan utas ini dengan contoh kode berikut.

  • CurrentUser - menyimpan id pengguna saat ini di ThreadLocal
  • TestService- Layanan sederhana dengan metode - getUser()untuk mengambil pengguna saat ini dari CurrentUser.
  • TestThread - kelas ini digunakan untuk membuat banyak utas dan mengatur id pengguna secara bersamaan

.

public class CurrentUser

public class CurrentUser {
private static final ThreadLocal<String> CURRENT = new ThreadLocal<String>();

public static ThreadLocal<String> getCurrent() {
    return CURRENT;
}

public static void setCurrent(String user) {
    CURRENT.set(user);
}

}

public class TestService {

public String getUser() {
    return CurrentUser.getCurrent().get();
}

}

.

import java.util.ArrayList;
import java.util.List;

public class TestThread {

public static void main(String[] args) {

  List<Integer> integerList = new ArrayList<>();

  //creates a List of 100 integers
  for (int i = 0; i < 100; i++) {

    integerList.add(i);
  }

  //parallel stream to test concurrent thread execution
  integerList.parallelStream().forEach(intValue -> {

    //All concurrent thread will set the user as "intValue"
    CurrentUser.setCurrent("" + intValue);
    //Thread creates a sample instance for TestService class
    TestService testService = new TestService();
    //Print the respective thread name along with "intValue" value and current user. 
    System.out.println("Start-"+Thread.currentThread().getName()+"->"+intValue + "->" + testService.getUser());

    try {
      //all concurrent thread will wait for 3 seconds
      Thread.sleep(3000l);
    } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }

    //Print the respective thread name along with "intValue" value and current user.
    System.out.println("End-"+Thread.currentThread().getName()+"->"+intValue + "->" + testService.getUser());
  });

}

}

.

Jalankan kelas utama TestThread. Keluaran -

Start-main->62->62
Start-ForkJoinPool.commonPool-worker-2->31->31
Start-ForkJoinPool.commonPool-worker-3->81->81
Start-ForkJoinPool.commonPool-worker-1->87->87
End-main->62->62
End-ForkJoinPool.commonPool-worker-1->87->87
End-ForkJoinPool.commonPool-worker-2->31->31
End-ForkJoinPool.commonPool-worker-3->81->81
Start-ForkJoinPool.commonPool-worker-2->32->32
Start-ForkJoinPool.commonPool-worker-3->82->82
Start-ForkJoinPool.commonPool-worker-1->88->88
Start-main->63->63
End-ForkJoinPool.commonPool-worker-1->88->88
End-main->63->63
...

Ringkasan analisis

  1. utas "main" dimulai dan setel pengguna saat ini sebagai "62", paralel "ForkJoinPool.commonPool-worker-2" thread dimulai dan set pengguna saat ini sebagai "31", paralel "ForkJoinPool.commonPool-worker-3" dimulai dan set saat ini pengguna sebagai "81", paralel "ForkJoinPool.commonPool-worker-1" dimulai dan set pengguna saat ini sebagai "87" Start-main-> 62-> 62 Start-ForkJoinPool.commonPool-worker-2-> 31-> 31 Start-ForkJoinPool.commonPool-worker-3-> 81-> 81 Start-ForkJoinPool.commonPool-worker-1-> 87-> 87
  2. Semua utas di atas akan tidur selama 3 detik
  3. maineksekusi berakhir dan cetak pengguna saat ini sebagai "62", ForkJoinPool.commonPool-worker-1eksekusi paralel berakhir dan cetak pengguna saat ini sebagai "87", ForkJoinPool.commonPool-worker-2eksekusi paralel berakhir dan cetak pengguna saat ini sebagai "31", ForkJoinPool.commonPool-worker-3eksekusi paralel berakhir dan cetak pengguna saat ini sebagai "81"

Kesimpulan

Concurrent Threads dapat mengambil userid yang benar meskipun telah dinyatakan sebagai "ThreadLocal final statis"

shijin raj
sumber