Kapan dan bagaimana saya harus menggunakan variabel ThreadLocal?

877

Kapan saya harus menggunakan ThreadLocalvariabel?

Bagaimana ini digunakan?

Dengarkan
sumber
11
Jika Anda menggunakan ThreadLocal, jangan pernah menulis pembungkus di atasnya !!! Setiap dan setiap pengembang yang ingin menggunakan variabel harus tahu itu adalah 'ThreadLocal'
Bukan bug
2
@Notabug Jika Anda eksplisit, Anda dapat memberi nama variabel Anda sehingga Anda berurusan dengan nilai ThreadLocal, misalnya RequestUtils.getCurrentRequestThreadLocal(). Tidak mengatakan ini sangat elegan, tetapi ini berasal dari kenyataan bahwa ThreadLocal sendiri tidak terlalu elegan dalam banyak kasus.
whirlwin
@Sasha Dapatkah ThreadLocal digunakan untuk menyimpan jejak eksekusi
gaurav

Jawaban:

864

Salah satu kemungkinan penggunaan (dan umum) adalah ketika Anda memiliki beberapa objek yang tidak aman-utas, tetapi Anda ingin menghindari sinkronisasi akses ke objek tersebut (saya sedang melihat Anda, SimpleDateFormat ). Sebagai gantinya, berikan masing-masing utas contohnya sendiri dari objek.

Sebagai contoh:

public class Foo
{
    // SimpleDateFormat is not thread-safe, so give one to each thread
    private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){
        @Override
        protected SimpleDateFormat initialValue()
        {
            return new SimpleDateFormat("yyyyMMdd HHmm");
        }
    };

    public String formatIt(Date date)
    {
        return formatter.get().format(date);
    }
}

Dokumentasi .

terlalu banyak berpikir
sumber
160
Alternatif lain untuk sinkronisasi atau threadlocal adalah menjadikan variabel sebagai variabel lokal. Vars lokal selalu aman. Saya berasumsi bahwa itu adalah praktik yang buruk untuk membuat DateFormats lokal karena harganya mahal untuk dibuat, tetapi saya belum pernah melihat metrik yang solid tentang topik ini.
Julien Chastang
18
Ini harga tinggi untuk membayar peretasan SimpleDateFormat. Mungkin lebih baik menggunakan alternatif yang aman . Jika Anda setuju bahwa lajang buruk maka ThreadLocalbahkan lebih buruk.
Alexander Ryzhov
4
@ overthink Apakah ada alasan untuk menyatakan ThreadLocal statis dan final, maksud saya kinerja atau sesuatu?
Sachin Gorade
4
Metode ThreadLocal.get () akan memanggil ThreadLocal.initialValue () (sekali) untuk setiap utas, yang berarti bahwa objek SimpleDateFormat dibuat untuk setiap utas. Bukankah lebih baik hanya menggunakan SimpleDateFormat sebagai variabel lokal (karena kita tidak harus berurusan dengan masalah pengumpulan sampah)?
sudeepdino008
3
Hanya berhati-hatilah untuk tidak menggunakan inisialisasi penjepit ganda untuk SimpleDateFormat Anda karena itu akan membuat kelas anonim sehingga pemuat kelas Anda tidak bisa menjadi sampah yang dikumpulkan. kebocoran memori: kembalikan SimpleDateFormat () {{applyPattern ("yyyyMMdd HHmm")}} baru;
user1944408
427

Karena a ThreadLocaladalah referensi ke data dalam suatu yang diberikan Thread, Anda dapat berakhir dengan kebocoran classloading saat menggunakan ThreadLocals di server aplikasi menggunakan kumpulan thread. Anda harus sangat berhati-hati membersihkan setiap ThreadLocals Anda get()atau set()dengan menggunakan ThreadLocal'sremove() metode.

Jika Anda tidak membersihkan setelah selesai, referensi apa pun yang dipegangnya ke kelas yang dimuat sebagai bagian dari webapp yang dikerahkan akan tetap berada di tumpukan permanen dan tidak akan pernah mengumpulkan sampah. Menggunakan kembali / tidak menggunakan webapp tidak akan membersihkan masing-masing Threadreferensi ke kelas webapp Anda karenaThread ini bukan sesuatu yang dimiliki oleh webapp Anda. Setiap penyebaran yang berurutan akan menciptakan instance baru dari kelas yang tidak akan pernah menjadi sampah yang dikumpulkan.

Anda akan berakhir dengan pengecualian dari memori karena java.lang.OutOfMemoryError: PermGen spacedan setelah beberapa googling mungkin hanya akan meningkat-XX:MaxPermSize bukannya memperbaiki bug.

Jika Anda akhirnya mengalami masalah ini, Anda dapat menentukan utas dan kelas mana yang mempertahankan referensi ini dengan menggunakan Memory Analyzer Eclipse dan / atau dengan mengikuti panduan dan tindak lanjut Frank Kieviet .

Pembaruan: Menemukan kembali entri blog Alex Vasseur yang membantu saya melacak beberapa ThreadLocalmasalah yang saya alami.

Phil M
sumber
11
Alex Vasseur memindahkan blognya. Berikut ini tautan saat ini ke artikel kebocoran memori.
Kenster
12
Utas yang terhubung dengan Julien telah pindah ke sini , dan layak dibaca ...
Donal Fellows
28
Ini adalah banyak sekali upvotes untuk sebuah jawaban yang, meskipun informatif, sama sekali tidak benar-benar menjawab pertanyaan.
Robin
8
Sekarang PermGen terbunuh oleh Java 8, apakah ini mengubah jawaban ini dengan cara apa pun?
Mesin Cuci Jahat
13
@Robin: Saya tidak setuju. Pertanyaannya adalah tentang bagaimana menggunakan ThreadLocal dengan benar, untuk mendapatkan pemahaman lengkap tentang konsep apa pun (ThreadLocal di sini) juga penting untuk memahami bagaimana tidak menggunakannya dan risiko menggunakannya secara sembarangan, dan itulah jawaban Phil. Saya senang dia membahas poin yang tidak tercakup dalam jawaban lain. Semua suara itu layak diterima. Saya percaya SO harus membangun pemahaman konsep daripada hanya menjadi situs QA.
Saurabh Patil
166

Banyak kerangka kerja menggunakan ThreadLocals untuk mempertahankan beberapa konteks yang terkait dengan utas saat ini. Misalnya ketika transaksi saat ini disimpan dalam ThreadLocal, Anda tidak perlu meneruskannya sebagai parameter melalui setiap pemanggilan metode, jika ada orang di tumpukan yang membutuhkan akses ke sana. Aplikasi web mungkin menyimpan informasi tentang permintaan dan sesi saat ini di ThreadLocal, sehingga aplikasi memiliki akses mudah ke sana. Dengan Guice Anda dapat menggunakan ThreadLocals saat menerapkan cakupan khusus untuk objek yang diinjeksi ( cakupan servlet default Guice kemungkinan besar menggunakannya juga).

ThreadLocals adalah salah satu jenis variabel global (walaupun sedikit kurang jahat karena dibatasi pada satu utas), jadi Anda harus berhati-hati saat menggunakannya untuk menghindari efek samping yang tidak diinginkan dan kebocoran memori. Rancang API Anda sehingga nilai ThreadLocal akan selalu dihapus secara otomatis saat tidak diperlukan lagi dan penggunaan API yang salah tidak akan mungkin dilakukan (misalnya seperti ini ). ThreadLocals dapat digunakan untuk membuat kode lebih bersih, dan dalam beberapa kasus yang jarang mereka adalah satu-satunya cara untuk membuat sesuatu bekerja (proyek saya saat ini memiliki dua kasus seperti itu; mereka didokumentasikan di sini di bawah "Bidang Statis dan Variabel Global").

Esko Luontola
sumber
4
Begitulah cara kerangka exPOJO (www.expojo.com) memungkinkan akses ke ORM Session / PersistenceManager tanpa perlu overhead penjelasan dan injeksi. Ini semacam 'injeksi ulir' dan bukannya 'injeksi objek'. Ini memberikan akses ke dependensi tanpa persyaratan (dan overhead) agar mereka tertanam di setiap objek yang mungkin membutuhkan dependensi tersebut. Sungguh menakjubkan bagaimana 'ringan' Anda dapat membuat kerangka DI ketika Anda menggunakan injeksi benang bukannya DI klasik (mis., Spring dll,)
Volksman
27
Kenapa aku harus menggulir ke bawah sejauh ini untuk menemukan jawaban penting ini !?
Jeremy Stein
1
@ Esko, Dalam proyek Anda, Anda menggunakan kode hash ThreadLocal di dan sederajat tidak diperlukan. Pendekatan yang lebih baik adalah membuat atau meneruskan referensi ke fungsi kode hash / sama dengan kapan pun dibutuhkan. Dengan cara ini, Anda dapat sepenuhnya menghindari kebutuhan peretasan ThreadLocal.
Pacerier
1
@JeremyStein, Jawaban yang lebih baik ada di stackoverflow.com/a/817911/632951 dan stackoverflow.com/a/17398338/632951 dan stackoverflow.com/a/818364/632951
Pacerier
1
Ini adalah penjelasan terbaik tentang penggunaan TL.
Dexter
52

Di Jawa, jika Anda memiliki datum yang dapat bervariasi per-utas, pilihan Anda adalah meneruskan datum itu ke setiap metode yang membutuhkan (atau mungkin perlu), atau untuk mengaitkan datum dengan utas. Melewati datum ke mana-mana mungkin bisa dilakukan jika semua metode Anda sudah perlu melewati variabel "konteks" yang umum.

Jika bukan itu masalahnya, Anda mungkin tidak ingin mengacaukan tanda tangan metode Anda dengan parameter tambahan. Di dunia non-utas, Anda bisa menyelesaikan masalah dengan Java yang setara dengan variabel global. Dalam kata berulir, ekuivalen dari variabel global adalah variabel thread-lokal.

pengguna100464
sumber
13
Jadi, Anda harus menghindari utas penduduk setempat dengan cara yang sama seperti Anda menghindari global. Saya tidak mungkin menerima bahwa tidak apa-apa untuk membuat global (utas lokal) alih-alih memberikan nilai sekitar, orang tidak suka karena sering mengungkapkan masalah arsitektur yang tidak ingin mereka perbaiki.
Juan Mendes
10
Mungkin ... Ini bisa berguna, jika Anda memiliki basis kode besar yang sudah ada yang harus Anda tambahkan datum baru yang harus diedarkan di mana - mana , misalnya konteks sesi, transaksi db, transaksi pengguna masuk, dll. .
Cornel Masson
6
Kudos untuk menggunakan datum, bukan data.
dalam
Ini membuat saya penasaran. 'datum' is the singular form and 'data' is the plural form.
Siddhartha
23

Ada contoh yang sangat bagus dalam buku Java Concurrency in Practice . Di mana penulis ( Joshua Bloch ) menjelaskan bagaimana pengurungan Thread adalah salah satu cara paling sederhana untuk mencapai keamanan thread dan ThreadLocal adalah cara yang lebih formal untuk mempertahankan penguraian thread. Pada akhirnya ia juga menjelaskan bagaimana orang dapat menyalahgunakannya dengan menggunakannya sebagai variabel global.

Saya telah menyalin teks dari buku tersebut tetapi kode 3.10 hilang karena tidak terlalu penting untuk memahami di mana ThreadLocal harus digunakan.

Variabel thread-lokal sering digunakan untuk mencegah berbagi dalam desain berdasarkan Singletons yang dapat berubah atau variabel global. Misalnya, aplikasi single-threaded dapat mempertahankan koneksi basis data global yang diinisialisasi pada saat startup untuk menghindari keharusan melewati Koneksi ke setiap metode. Karena koneksi JDBC mungkin tidak aman-utas, aplikasi multithreaded yang menggunakan koneksi global tanpa koordinasi tambahan juga tidak aman-utas. Dengan menggunakan ThreadLocal untuk menyimpan koneksi JDBC, seperti pada ConnectionHolder pada Listing 3.10, setiap utas akan memiliki koneksi sendiri.

ThreadLocal banyak digunakan dalam mengimplementasikan kerangka kerja aplikasi. Misalnya, kontainer J2EE mengaitkan konteks transaksi dengan utas pelaksana selama durasi panggilan EJB. Ini mudah diimplementasikan menggunakan Thread-Local statis memegang konteks transaksi: ketika kode kerangka kerja perlu menentukan transaksi apa yang sedang berjalan, itu mengambil konteks transaksi dari ThreadLocal ini. Ini nyaman karena mengurangi kebutuhan untuk menyampaikan informasi konteks eksekusi ke setiap metode, tetapi memasangkan setiap kode yang menggunakan mekanisme ini ke kerangka kerja.

Sangat mudah untuk menyalahgunakan ThreadLocal dengan memperlakukan properti pengurutan utangnya sebagai lisensi untuk menggunakan variabel global atau sebagai sarana untuk menciptakan argumen metode "tersembunyi". Seperti variabel global, variabel thread-lokal dapat mengurangi usabilitas dan memperkenalkan kopling tersembunyi di antara kelas, dan karenanya harus digunakan dengan hati-hati.

Rakesh Chauhan
sumber
18

Pada dasarnya, ketika Anda membutuhkan nilai variabel untuk bergantung pada utas saat ini dan itu tidak nyaman bagi Anda untuk melampirkan nilai pada utas dengan cara lain (misalnya, utas subkelas).

Kasus khas adalah di mana beberapa kerangka kerja lain telah membuat utas bahwa kode Anda berjalan, misalnya wadah servlet, atau di mana lebih masuk akal untuk menggunakan ThreadLocal karena variabel Anda kemudian "di tempat logis" (daripada variabel tergantung dari subclass Thread atau di beberapa peta hash lainnya).

Di situs web saya, saya memiliki beberapa diskusi lebih lanjut dan contoh kapan menggunakan ThreadLocal yang mungkin juga menarik.

Beberapa orang menganjurkan menggunakan ThreadLocal sebagai cara untuk melampirkan "ID utas" ke setiap utas dalam algoritme konkuren tertentu di mana Anda memerlukan nomor utas (lihat mis. Herlihy & Shavit). Dalam kasus seperti itu, periksa apakah Anda benar-benar mendapatkan manfaat!

Neil Coffey
sumber
Dapatkah ThreadLocal digunakan untuk menyimpan jejak eksekusi
gaurav
13
  1. ThreadLocal di Jawa telah diperkenalkan pada JDK 1.2 tetapi kemudian dihasilkan di JDK 1.5 untuk memperkenalkan keamanan tipe pada variabel ThreadLocal.

  2. ThreadLocal dapat dikaitkan dengan lingkup Thread, semua kode yang dieksekusi oleh Thread memiliki akses ke variabel ThreadLocal tetapi dua utas tidak dapat saling melihat variabel ThreadLocal.

  3. Setiap utas memegang salinan eksklusif variabel ThreadLocal yang menjadi memenuhi syarat untuk pengumpulan Sampah setelah utas selesai atau mati, secara normal atau karena Pengecualian, Mengingat variabel ThreadLocal itu tidak memiliki referensi langsung lainnya.

  4. Variabel ThreadLocal di Jawa umumnya bidang statis pribadi di Kelas dan mempertahankan statusnya di dalam Thread.

Baca selengkapnya: ThreadLocal in Java - Contoh Program dan Tutorial

Abhijit Gaikwad
sumber
Dapatkah ThreadLocal digunakan untuk menyimpan jejak eksekusi
gaurav
11

Dokumentasi mengatakan dengan sangat baik: "setiap thread yang mengakses [variabel thread-lokal] (melalui metode get atau set-nya) memiliki salinan variabel sendiri yang diinisialisasi secara independen".

Anda menggunakan satu ketika setiap utas harus memiliki salinan sesuatu. Secara default, data dibagi di antara utas.

RichieHindle
sumber
19
Secara default, objek statis, atau objek yang secara eksplisit dilewatkan di antara utas tempat kedua utas memiliki referensi ke objek yang sama, dibagikan. Objek yang dideklarasikan secara lokal di utas tidak dibagikan (mereka lokal ke tumpukan utas) Hanya ingin mengklarifikasi itu.
Ian Varley
@IanVarley: Terima kasih telah menunjukkan itu. Jadi jika kita memiliki variabel yang dideklarasikan dan digunakan di kelas MyThread, maka apakah kita perlu ThreadLocal dalam kasus itu? Contoh: class MyThread extends Thread {String var1 ;, int var2; } dalam hal ini var1 dan var2 akan menjadi bagian dari stack Thread sendiri dan tidak dibagi, jadi apakah kita memerlukan ThreadLocal dalam kasus ini? Saya mungkin benar-benar salah dalam pemahaman saya, mohon panduan.
Jayesh
4
Jika setiap utas ingin memiliki salinannya sendiri, mengapa tidak dapat dideklarasikan secara lokal saja (yang selalu aman utas)?
sudeepdino008
@ sudeepdino008 Anda tidak selalu memiliki kendali atas penciptaan utas tersebut (kerangka webapp, pustaka threadpool)
JMess
10

Server webapp dapat menyimpan kumpulan utas, dan ThreadLocalvar harus dihapus sebelum menanggapi klien, sehingga utas saat ini dapat digunakan kembali oleh permintaan berikutnya.

znlyj
sumber
8

Dua kasus penggunaan di mana variabel threadlocal dapat digunakan -
1- Ketika kita memiliki persyaratan untuk mengaitkan status dengan utas (misalnya, ID pengguna atau ID Transaksi). Itu biasanya terjadi dengan aplikasi web yang setiap permintaan pergi ke servlet memiliki transaksiID unik yang terkait dengannya.

// This class will provide a thread local variable which
// will provide a unique ID for each thread
class ThreadId {
    // Atomic integer containing the next thread ID to be assigned
    private static final AtomicInteger nextId = new AtomicInteger(0);

    // Thread local variable containing each thread's ID
    private static final ThreadLocal<Integer> threadId =
        ThreadLocal.<Integer>withInitial(()-> {return nextId.getAndIncrement();});

    // Returns the current thread's unique ID, assigning it if necessary
    public static int get() {
        return threadId.get();
    }
}

Perhatikan bahwa di sini metode withInitial diimplementasikan menggunakan ekspresi lambda.
2 - Case use lain adalah ketika kita ingin memiliki instance thread aman dan kami tidak ingin menggunakan sinkronisasi karena biaya kinerja dengan sinkronisasi lebih banyak. Salah satu kasusnya adalah ketika SimpleDateFormat digunakan. Karena SimpleDateFormat bukan thread yang aman, maka kami harus menyediakan mekanisme untuk membuatnya thread yang aman.

public class ThreadLocalDemo1 implements Runnable {
    // threadlocal variable is created
    private static final ThreadLocal<SimpleDateFormat> dateFormat = new ThreadLocal<SimpleDateFormat>(){
        @Override
        protected SimpleDateFormat initialValue(){
            System.out.println("Initializing SimpleDateFormat for - " + Thread.currentThread().getName() );
            return new SimpleDateFormat("dd/MM/yyyy");
        }
    };

    public static void main(String[] args) {
        ThreadLocalDemo1 td = new ThreadLocalDemo1();
        // Two threads are created
        Thread t1 = new Thread(td, "Thread-1");
        Thread t2 = new Thread(td, "Thread-2");
        t1.start();
        t2.start();
    }

    @Override
    public void run() {
        System.out.println("Thread run execution started for " + Thread.currentThread().getName());
        System.out.println("Date formatter pattern is  " + dateFormat.get().toPattern());
        System.out.println("Formatted date is " + dateFormat.get().format(new Date()));
    } 

}
infoj
sumber
Saya masih tidak melihat manfaat dari menggunakan ThreadLocal untuk menghasilkan id unik di contoh satu jika semua tujuan Anda adalah mengembalikan nilai integer unik tambahan. `` `kelas publik ThreadId {// Bilangan bulat atom yang berisi ID utas berikutnya yang akan ditugaskan sebagai AtomicInteger final static nextId = new AtomicInteger (0); // Mengembalikan ID unik utas saat ini, menugaskannya jika perlu int static publik () {return nextId..getAndIncrement (); }} `` `
dashenswen
7

Sejak rilis Java 8, ada lebih banyak cara deklaratif untuk menginisialisasi ThreadLocal:

ThreadLocal<Cipher> local = ThreadLocal.withInitial(() -> "init value");

Sampai rilis Java 8 Anda harus melakukan hal berikut:

ThreadLocal<String> local = new ThreadLocal<String>(){
    @Override
    protected String initialValue() {
        return "init value";
    }
};

Selain itu, jika metode instantiasi (konstruktor, metode pabrik) kelas yang digunakan untuk ThreadLocaltidak mengambil parameter apa pun, Anda dapat menggunakan referensi metode (diperkenalkan di Jawa 8):

class NotThreadSafe {
    // no parameters
    public NotThreadSafe(){}
}

ThreadLocal<NotThreadSafe> container = ThreadLocal.withInitial(NotThreadSafe::new);

Catatan: Evaluasi malas karena Anda melewati java.util.function.Supplierlambda yang dievaluasi hanya ketika ThreadLocal#getdipanggil tetapi nilai sebelumnya tidak dievaluasi.

Andrii Abramov
sumber
5

Anda harus sangat berhati-hati dengan pola ThreadLocal. Ada beberapa sisi utama utama seperti yang disebutkan Phil, tetapi yang tidak disebutkan adalah memastikan bahwa kode yang mengatur konteks ThreadLocal tidak "masuk kembali."

Hal-hal buruk dapat terjadi ketika kode yang mengatur informasi dijalankan untuk kedua atau ketiga kalinya karena informasi di utas Anda dapat mulai bermutasi ketika Anda tidak mengharapkannya. Jadi berhati-hatilah untuk memastikan informasi ThreadLocal belum diatur sebelum Anda mengaturnya lagi.

Jeff Richley
sumber
2
Re-entrancy bukan masalah jika kode disiapkan untuk menghadapinya. Pada entri, catat apakah variabel sudah ditetapkan, dan saat keluar, kembalikan nilai sebelumnya (jika ada), atau hapus (jika tidak).
supercat
1
@ Jeff, Bung, itu benar dari setiap kode yang Anda tulis, bukan hanya ThreadLocalpola. Jika Anda melakukannya F(){ member=random(); F2(); write(member); }dan F2 menimpa anggota dengan nilai baru, maka jelas write(member)tidak akan lagi menulis nomor yang telah Anda random()edit. Ini secara harfiah hanya akal sehat. Demikian pula, jika Anda melakukannya F(){ F(); }, dapatkan keberuntungan dengan loop tak terbatas Anda! Ini benar di mana-mana dan tidak khusus untuk ThreadLocal.
Pacerier
@ Jeff, contoh kode?
Pacerier
5

kapan?

Ketika suatu objek tidak aman terhadap thread, alih-alih sinkronisasi yang menghambat skalabilitas, berikan satu objek ke setiap utas dan pertahankan cakupannya, yaitu ThreadLocal. Salah satu objek yang paling sering digunakan tetapi bukan thread-safe adalah Koneksi database dan JMSConnection.

Bagaimana?

Salah satu contohnya adalah kerangka kerja Spring menggunakan ThreadLocal banyak untuk mengelola transaksi di belakang layar dengan menjaga objek koneksi ini dalam variabel ThreadLocal. Pada level tinggi, ketika transaksi dimulai, ia mendapatkan koneksi (dan menonaktifkan komit otomatis) dan menyimpannya di ThreadLocal. pada panggilan db selanjutnya ia menggunakan koneksi yang sama untuk berkomunikasi dengan db. Pada akhirnya, dibutuhkan koneksi dari ThreadLocal dan melakukan (atau mengembalikan) transaksi dan melepaskan koneksi.

Saya pikir log4j juga menggunakan ThreadLocal untuk mempertahankan MDC.

Batu gamping
sumber
5

ThreadLocal berguna, ketika Anda ingin memiliki beberapa status yang tidak boleh dibagikan di antara utas yang berbeda, tetapi harus dapat diakses dari setiap utas selama seluruh masa pakainya.

Sebagai contoh, bayangkan aplikasi web, di mana setiap permintaan dilayani oleh utas yang berbeda. Bayangkan bahwa untuk setiap permintaan Anda membutuhkan data beberapa kali, yang cukup mahal untuk dihitung. Namun, data itu mungkin telah berubah untuk setiap permintaan yang masuk, yang berarti bahwa Anda tidak dapat menggunakan cache polos. Solusi sederhana dan cepat untuk masalah ini adalah memiliki ThreadLocalvariabel yang menahan akses ke data ini, sehingga Anda harus menghitungnya hanya sekali untuk setiap permintaan. Tentu saja, masalah ini juga dapat diselesaikan tanpa menggunakanThreadLocal , tetapi saya menyusunnya untuk tujuan ilustrasi.

Yang mengatakan, ada dalam pikiran bahwa ThreadLocalpada dasarnya adalah bentuk negara global. Akibatnya, ia memiliki banyak implikasi lain dan harus digunakan hanya setelah mempertimbangkan semua solusi lain yang mungkin.

Dimos
sumber
ThreadLocals BUKAN status global kecuali Anda membuatnya status global. Mereka secara praktis selalu dapat diakses sumber daya tumpukan. Anda dapat mensimulasikan thread lokal dengan hanya mengirimkan variabel itu di semua metode Anda (yang terbelakang); itu tidak menjadikannya negara global ...
Enerccio
Ini adalah bentuk negara global, dalam arti dapat diakses dari mana saja dalam kode (dalam konteks utas yang sama). Ini datang dengan semua dampak, seperti tidak dapat beralasan tentang siapa yang membaca dan menulis nilai ini. Menggunakan parameter fungsi bukan hal terbelakang, itu praktik yang baik mempromosikan antarmuka yang bersih. Namun, saya setuju dengan Anda bahwa melewatkan parameter di seluruh kedalaman basis kode Anda adalah bau kode. Tapi, saya juga percaya bahwa dalam banyak kesempatan penggunaan ThreadLocal adalah bau kode di tempat pertama yang membawa Anda ke sini, jadi ini yang harus dipertimbangkan kembali.
Dimos
Ini hanya bisa menjadi bagian dari objek, tidak harus digunakan secara global. Tentu, Anda mendapatkan sedikit overhead, tetapi jika beberapa objek harus memiliki status berbeda per utas, Anda dapat menggunakan ThreadLocalsebagai bidang objek ...
Enerccio
Kamu benar. Saya mungkin telah menemukan beberapa penyalahgunaan ThreadLocal, di mana itu dapat diakses secara global. Seperti yang Anda katakan, itu masih bisa digunakan sebagai bidang kelas yang membatasi visibilitas.
Dimos
4

Tidak ada yang benar-benar baru di sini, tetapi saya menemukan hari ini yang ThreadLocalsangat berguna ketika menggunakan Validasi Bean dalam aplikasi web. Pesan validasi dilokalkan, tetapi secara default digunakan Locale.getDefault(). Anda dapat mengkonfigurasi Validatordengan yang berbeda MessageInterpolator, tetapi tidak ada cara untuk menentukan Localekapan Anda menelepon validate. Jadi Anda bisa membuat statis ThreadLocal<Locale>(atau lebih baik lagi, wadah umum dengan hal-hal lain yang mungkin perlu ThreadLocaldan kemudian memiliki kebiasaan Anda MessageInterpolatormemilih Localedari itu. Langkah selanjutnya adalah menulis ServletFilteryang menggunakan nilai sesi atau request.getLocale()untuk memilih lokal dan toko itu dalam ThreadLocalreferensi Anda .

Colselaw
sumber
4

Seperti yang disebutkan oleh @unknown (google), penggunaannya adalah untuk menentukan variabel global di mana nilai yang dirujuk dapat menjadi unik di setiap utas. Penggunaannya biasanya mencakup menyimpan beberapa jenis informasi kontekstual yang terkait dengan utas eksekusi saat ini.

Kami menggunakannya di lingkungan Java EE untuk meneruskan identitas pengguna ke kelas yang tidak diketahui Java EE (tidak memiliki akses ke HttpSession, atau EJB SessionContext). Dengan cara ini kode, yang membuat penggunaan identitas untuk operasi berbasis keamanan, dapat mengakses identitas dari mana saja, tanpa harus secara eksplisit meneruskannya dalam setiap pemanggilan metode.

Siklus permintaan / respons operasi di sebagian besar panggilan Java EE membuat jenis penggunaan ini mudah karena memberikan titik masuk dan keluar yang ditentukan dengan baik untuk mengatur dan menghapus ThreadLocal.

Robin
sumber
4

ThreadLocal akan memastikan mengakses objek yang dapat diubah oleh beberapa utas dalam metode yang tidak disinkronkan disinkronkan, berarti membuat objek yang dapat diubah menjadi tidak berubah dalam metode ini.

Ini dicapai dengan memberikan instance baru dari objek yang dapat diubah untuk setiap utas yang mencoba mengaksesnya. Jadi ini adalah salinan lokal untuk setiap utas. Ini beberapa hack membuat variabel instan dalam metode yang akan diakses seperti variabel lokal. Ketika Anda mengetahui metode variabel lokal hanya tersedia untuk utas, satu perbedaan adalah; metode variabel lokal tidak akan tersedia untuk utas setelah eksekusi metode selesai di mana objek yang dapat ditukar yang dibagikan dengan threadlocal akan tersedia di beberapa metode hingga kami membersihkannya.

Menurut definisi:

Kelas ThreadLocal di Java memungkinkan Anda membuat variabel yang hanya bisa dibaca dan ditulis oleh utas yang sama. Jadi, bahkan jika dua utas mengeksekusi kode yang sama, dan kode tersebut memiliki referensi ke variabel ThreadLocal, maka kedua utas tersebut tidak dapat melihat variabel ThreadLocal satu sama lain.

Masing-masing Threaddi java berisi ThreadLocalMapdi dalamnya.
Dimana

Key = One ThreadLocal object shared across threads.
value = Mutable object which has to be used synchronously, this will be instantiated for each thread.

Mencapai ThreadLocal:

Sekarang buat kelas pembungkus untuk ThreadLocal yang akan menampung objek yang bisa berubah seperti di bawah ini (dengan atau tanpa initialValue()).
Sekarang pengambil dan penyetel dari pembungkus ini akan bekerja pada instance threadlocal alih-alih objek yang dapat diubah.

Jika pengambil () dari threadlocal tidak menemukan nilai apa pun di dalam threadlocalmap dari Thread; maka ia akan memanggil initialValue () untuk mendapatkan salinan pribadinya sehubungan dengan utas.

class SimpleDateFormatInstancePerThread {

    private static final ThreadLocal<SimpleDateFormat> dateFormatHolder = new ThreadLocal<SimpleDateFormat>() {

        @Override
        protected SimpleDateFormat initialValue() {
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd") {
                UUID id = UUID.randomUUID();
                @Override
                public String toString() {
                    return id.toString();
                };
            };
            System.out.println("Creating SimpleDateFormat instance " + dateFormat +" for Thread : " + Thread.currentThread().getName());
            return dateFormat;
        }
    };

    /*
     * Every time there is a call for DateFormat, ThreadLocal will return calling
     * Thread's copy of SimpleDateFormat
     */
    public static DateFormat getDateFormatter() {
        return dateFormatHolder.get();
    }

    public static void cleanup() {
        dateFormatHolder.remove();
    }
}

Sekarang wrapper.getDateFormatter()akan memanggil threadlocal.get()dan itu akan memeriksa contoh currentThread.threadLocalMapberisi ini (threadlocal).
Jika ya kembalikan nilai (SimpleDateFormat) untuk instance threadlocal yang sesuai atau
tambahkan peta dengan instance threadlocal ini, initialValue ().

Dengan ini keselamatan benang dicapai pada kelas yang bisa berubah ini; oleh masing-masing thread bekerja dengan instance yang dapat berubah sendiri tetapi dengan instance ThreadLocal yang sama. Berarti Semua utas akan membagikan instance ThreadLocal yang sama sebagai kunci, tetapi contoh SimpleDateFormat yang berbeda sebagai nilai.

https://github.com/skanagavelu/yt.tech/blob/master/src/ThreadLocalTest.java

Kanagavelu Sugumar
sumber
3

Variabel thread-lokal sering digunakan untuk mencegah berbagi dalam desain berdasarkan Singletons yang dapat berubah atau variabel global.

Ini dapat digunakan dalam skenario seperti membuat koneksi JDBC terpisah untuk setiap utas saat Anda tidak menggunakan Connection Pool.

private static ThreadLocal<Connection> connectionHolder
           = new ThreadLocal<Connection>() {
      public Connection initialValue() {
           return DriverManager.getConnection(DB_URL);
          }
     };

public static Connection getConnection() {
      return connectionHolder.get();
} 

Saat Anda memanggil getConnection, itu akan mengembalikan koneksi yang terkait dengan utas itu. Hal yang sama dapat dilakukan dengan properti lain seperti dateformat, konteks transaksi yang tidak ingin Anda bagikan di antara utas.

Anda bisa juga menggunakan variabel lokal untuk hal yang sama, tetapi sumber daya ini biasanya membutuhkan waktu dalam pembuatan, jadi Anda tidak ingin membuatnya lagi dan lagi setiap kali Anda melakukan beberapa logika bisnis dengannya. Namun, nilai-nilai ThreadLocal disimpan dalam objek utas itu sendiri dan segera setelah utas dikumpulkan, nilai-nilai ini juga hilang.

Tautan ini menjelaskan penggunaan ThreadLocal dengan sangat baik.

bpjoshi
sumber
2
Dalam contoh ini adalah masalah utama: Siapa yang bertanggung jawab atas penutupan koneksi? Alih-alih membuat koneksi ini sebagai nilai awal, biarkan konsumen koneksi membuat koneksi secara eksplisit dan mengikatnya ke utas melalui ThreadLocal. Pembuat koneksi kemudian juga bertanggung jawab untuk menutup. Ini tidak jelas dalam contoh ini. Membuat dan mengikat koneksi juga bisa disembunyikan dalam kerangka sederhana oleh sesuatu seperti Transaction.begin () dan Transaction.end ().
Rick-Rainer Ludwig
2

Kelas ThreadLocal di Java memungkinkan Anda membuat variabel yang hanya bisa dibaca dan ditulis oleh utas yang sama. Jadi, bahkan jika dua utas mengeksekusi kode yang sama, dan kode tersebut memiliki referensi ke variabel ThreadLocal, maka kedua utas tersebut tidak dapat melihat variabel ThreadLocal satu sama lain.

Baca lebih banyak

Pritesh Patel
sumber
2

Ada 3 skenario untuk menggunakan pembantu kelas seperti SimpleDateFormat dalam kode multithread, yang terbaik adalah menggunakan ThreadLocal

Skenario

1- Menggunakan seperti objek share dengan bantuan kunci atau mekanisme sinkronisasi yang membuat aplikasi lambat

2- Menggunakan sebagai objek lokal di dalam suatu metode

Dalam skenario ini, jika kita memiliki 4 utas masing-masing memanggil metode 1000 kali maka kita memiliki
4000 objek SimpleDateFormat dibuat dan menunggu GC untuk menghapusnya

3 - Menggunakan ThreadLocal

jika kita memiliki 4 utas dan kita memberi contoh masing - masing satu contoh SimpleDateFormat
jadi kita punya 4 utas , 4 objek SimpleDateFormat.

Tidak perlu mekanisme kunci dan penciptaan dan penghancuran objek. (Kompleksitas waktu dan kompleksitas ruang)

mohsen
sumber
2

[Untuk Referensi] ThreadLocal tidak dapat menyelesaikan masalah pembaruan objek yang dibagikan. Disarankan untuk menggunakan objek staticThreadLocal yang dibagikan oleh semua operasi di utas yang sama. Metode [wajib] hapus () harus diimplementasikan oleh variabel ThreadLocal, terutama ketika menggunakan kumpulan utas di mana utas sering digunakan kembali. Jika tidak, ini dapat mempengaruhi logika bisnis selanjutnya dan menyebabkan masalah yang tidak terduga seperti kebocoran memori.

Muda
sumber
1

Caching, kadang-kadang Anda harus menghitung nilai banyak waktu yang sama sehingga dengan menyimpan set input terakhir ke suatu metode dan hasilnya Anda dapat mempercepat kode. Dengan menggunakan Thread Local Storage Anda tidak perlu memikirkan penguncian.

Ian Ringrose
sumber
1

ThreadLocal adalah fungsionalitas yang disediakan khusus oleh JVM untuk menyediakan ruang penyimpanan terisolasi hanya untuk utas. seperti nilai instance variabel terikat hanya untuk contoh kelas tertentu. setiap objek hanya memiliki nilai dan tidak dapat melihat nilai satu sama lain. begitu juga konsep variabel ThreadLocal, mereka adalah lokal untuk thread dalam arti contoh objek thread lain kecuali untuk yang membuatnya, tidak bisa melihatnya. Lihat disini

import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;


public class ThreadId {
private static final AtomicInteger nextId = new AtomicInteger(1000);

// Thread local variable containing each thread's ID
private static final ThreadLocal<Integer> threadId = ThreadLocal.withInitial(() -> nextId.getAndIncrement());


// Returns the current thread's unique ID, assigning it if necessary
public static int get() {
    return threadId.get();
}

public static void main(String[] args) {

    new Thread(() -> IntStream.range(1, 3).forEach(i -> {
        System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
    })).start();

    new Thread(() -> IntStream.range(1, 3).forEach(i -> {
        System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
    })).start();

    new Thread(() -> IntStream.range(1, 3).forEach(i -> {
        System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
    })).start();

}
}
Ajay Kumar
sumber
1

Threadlocal menyediakan cara yang sangat mudah untuk mencapai usabilitas objek dengan biaya nol.

Saya mengalami situasi di mana beberapa utas membuat gambar cache yang dapat diubah, pada setiap pemberitahuan pembaruan.

Saya menggunakan Threadlocal pada setiap utas, dan kemudian setiap utas hanya perlu mengatur ulang gambar lama dan kemudian memperbaruinya lagi dari cache pada setiap pemberitahuan pembaruan.

Objek yang dapat digunakan kembali secara normal dari kumpulan objek memiliki biaya keamanan ulir yang terkait dengannya, sementara pendekatan ini tidak memiliki satu pun.

Dev Amitabh
sumber
0

Coba contoh kecil ini, untuk merasakan variabel ThreadLocal:

public class Book implements Runnable {
    private static final ThreadLocal<List<String>> WORDS = ThreadLocal.withInitial(ArrayList::new);

    private final String bookName; // It is also the thread's name
    private final List<String> words;


    public Book(String bookName, List<String> words) {
        this.bookName = bookName;
        this.words = Collections.unmodifiableList(words);
    }

    public void run() {
        WORDS.get().addAll(words);
        System.out.printf("Result %s: '%s'.%n", bookName, String.join(", ", WORDS.get()));
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(new Book("BookA", Arrays.asList("wordA1", "wordA2", "wordA3")));
        Thread t2 = new Thread(new Book("BookB", Arrays.asList("wordB1", "wordB2")));
        t1.start();
        t2.start();
    }
}


Output konsol, jika thread BookA dilakukan terlebih dahulu:
Book BookA: 'wordA1, wordA2, wordA3'.
Buku Hasil B: 'kata B1, kata B2'.

Output konsol, jika utas BookB dilakukan terlebih dahulu:
Buku HasilB: 'wordB1, wordB2'.
Buku Hasil: 'wordA1, wordA2, wordA3'.

DigitShifter
sumber