Pertanyaan Kapan dan bagaimana saya harus menggunakan variabel ThreadLocal?


Kapan saya harus menggunakan ThreadLocal variabel?

Bagaimana cara menggunakannya?


780
2018-05-03 19:59


asal


Jawaban:


Satu kemungkinan penggunaan (dan umum) adalah ketika Anda memiliki beberapa objek yang tidak aman-thread, tetapi Anda ingin menghindari sinkronisasi akses ke objek itu (saya melihat Anda, SimpleDateFormat). Sebagai gantinya, beri setiap utas objeknya sendiri.

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.


777
2018-05-03 20:26



Sejak ThreadLocal adalah referensi ke data dalam yang diberikan Thread, Anda bisa berakhir dengan kebocoran classloading saat menggunakan ThreadLocals dalam server aplikasi yang menggunakan kumpulan thread. Anda harus sangat berhati-hati dalam membersihkannya ThreadLocals Anda get() atau set() dengan menggunakan ThreadLocal's remove() metode.

Jika Anda tidak membersihkan setelah selesai, semua referensi yang dimilikinya untuk kelas yang dimuat sebagai bagian dari apl web yang diterapkan akan tetap ada di timbunan permanen dan tidak akan pernah mengumpulkan sampah. Redeploying / undeploying webapp tidak akan membersihkan masing-masing ThreadReferensi ke kelas webapp Anda (es) sejak Thread bukan sesuatu yang dimiliki oleh webapp Anda. Setiap penempatan berturut-turut akan membuat instance baru dari kelas yang tidak akan pernah dikumpulkan.

Anda akan berakhir dengan pengecualian kehabisan memori karena java.lang.OutOfMemoryError: PermGen space dan setelah beberapa googling mungkin hanya akan bertambah -XX:MaxPermSize bukannya memperbaiki bug.

Jika Anda akhirnya mengalami masalah ini, Anda dapat menentukan untaian dan kelas mana yang mempertahankan referensi ini dengan menggunakan Eclipse's Memory Analyzer dan / atau dengan mengikuti Panduan Frank Kieviet dan mengikuti.

Pembaruan: Ditemukan kembali Entri blog Alex Vasseur yang membantu saya melacak beberapa ThreadLocal masalah yang saya hadapi.


380
2018-05-03 22:02



Banyak kerangka kerja menggunakan ThreadLocals untuk mempertahankan beberapa konteks yang terkait dengan rangkaian saat ini. Misalnya ketika transaksi saat ini disimpan dalam ThreadLocal, Anda tidak perlu melewatkannya sebagai parameter melalui setiap pemanggilan metode, jika seseorang yang menumpuk membutuhkan akses ke sana. Aplikasi web mungkin menyimpan informasi tentang permintaan saat ini dan sesi di ThreadLocal, sehingga aplikasi memiliki akses yang mudah kepada mereka. Dengan Guice Anda dapat menggunakan ThreadLocals saat menerapkan cakupan khusus untuk objek yang disuntikkan (default Guice lingkup servlet paling mungkin menggunakannya juga).

ThreadLocals adalah salah satu jenis variabel global (walaupun agak kurang jahat karena dibatasi untuk satu utas), jadi Anda harus berhati-hati saat menggunakannya untuk menghindari efek samping dan kebocoran memori yang tidak diinginkan. Desain API Anda sehingga nilai-nilai ThreadLocal akan selalu secara otomatis dihapus ketika mereka tidak lagi diperlukan dan bahwa penggunaan yang salah dari API tidak akan mungkin (misalnya seperti ini). ThreadLocals dapat digunakan untuk membuat kode 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 sini di bawah "Bidang Statis dan Variabel Global").


123
2018-05-04 13:36



Di Java, jika Anda memiliki datum yang dapat bervariasi per-utas, pilihan Anda adalah untuk melewatkan datum itu ke setiap metode yang membutuhkan (atau mungkin membutuhkan), atau menghubungkan datum dengan utas. Melewatkan datum di mana-mana mungkin bisa diterapkan jika semua metode Anda sudah perlu menyampaikan variabel "konteks" umum.

Jika itu tidak terjadi, Anda mungkin tidak ingin mengacaukan tanda tangan metode Anda dengan parameter tambahan. Di dunia yang tidak berulir, Anda bisa memecahkan masalah dengan Java yang setara dengan variabel global. Dalam kata berulir, ekuivalen variabel global adalah variabel untaian-lokal.


42
2018-05-03 20:20



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

Kasus tipikal adalah di mana beberapa kerangka lain telah membuat utas kode Anda sedang berjalan, mis. sebuah kontainer servlet, atau di mana itu hanya lebih masuk akal untuk menggunakan ThreadLocal karena variabel Anda kemudian "di tempat yang logis" (bukan variabel tergantung dari subclass Thread atau di beberapa peta hash lainnya).

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

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


13
2018-05-03 23:50



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

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

Variabel-variabel lokal sering digunakan untuk mencegah berbagi dalam desain berdasarkan Singleton yang bisa berubah atau variabel global. Sebagai contoh, aplikasi single-threaded mungkin mempertahankan koneksi database global yang diinisialisasi saat startup untuk menghindari keharusan untuk meneruskan Koneksi ke setiap metode. Karena koneksi JDBC mungkin tidak aman-thread, aplikasi multithread yang menggunakan koneksi global tanpa koordinasi tambahan juga tidak aman-thread. Dengan menggunakan ThreadLocal untuk menyimpan koneksi JDBC, seperti di ConnectionHolder di Listing 3.10, setiap thread akan memiliki koneksi sendiri.

ThreadLocal secara luas digunakan dalam mengimplementasikan kerangka kerja aplikasi. Misalnya, kontainer J2EE mengaitkan konteks transaksi dengan utas eksekusi selama durasi panggilan EJB. Ini mudah diimplementasikan menggunakan Thread-Lokal 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 meneruskan informasi konteks eksekusi ke dalam setiap metode, tetapi memasangkan kode apa pun yang menggunakan mekanisme ini ke kerangka kerja.

Sangat mudah menyalahgunakan ThreadLocal dengan memperlakukan properti penguncian benangnya sebagai lisensi untuk menggunakan variabel global atau sebagai sarana untuk membuat argumen metode “tersembunyi”. Seperti variabel global, variabel lokal-benang dapat mengurangi penggunaan kembali dan memperkenalkan kopling tersembunyi di antara kelas, dan oleh karena itu harus digunakan dengan hati-hati.


11
2017-10-04 09:28



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

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


9
2018-05-03 20:05