Rumah >Java >javaTutorial >Bagaimana untuk menggunakan kelas Java ThreadLocal
Seperti yang ditunjukkan dalam gambar:
Seterusnya, kami akan menggunakan contoh mudah untuk menunjukkan kepada anda penggunaan asas ThreadLocal
package cuit.pymjl.thradlocal; /** * @author Pymjl * @version 1.0 * @date 2022/7/1 10:56 **/ public class MainTest { static ThreadLocal<String> threadLocal = new ThreadLocal<>(); static void print(String str) { //打印当前线程中本地内存中本地变量的值 System.out.println(str + " :" + threadLocal.get()); //清除本地内存中的本地变量 threadLocal.remove(); } public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { //设置线程1中本地变量的值 threadLocal.set("thread1 local variable"); //调用打印方法 print("thread1"); //打印本地变量 System.out.println("after remove : " + threadLocal.get()); } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { //设置线程1中本地变量的值 threadLocal.set("thread2 local variable"); //调用打印方法 print("thread2"); //打印本地变量 System.out.println("after remove : " + threadLocal.get()); } }); t1.start(); t2.start(); } }
Hasil larian adalah seperti yang ditunjukkan dalam rajah:
Mari kita lihat dahulu struktur gambar rajah kelas kelas berkaitan ThreadLocal, sebagai ditunjukkan dalam rajah:
Seperti yang dapat dilihat daripada rajah ini, terdapat threadLocals dan inheritableThreadLocals dalam kelas ThreadLocalMap jenis, dan ThreadLocalMap ialah Hashmap tersuai. Secara lalai, kedua-dua pembolehubah ini dalam setiap utas adalah batal, dan ia dicipta hanya apabila utas semasa memanggil set atau dapatkan kaedah ThreadLocal untuk kali pertama. Sebenarnya, pembolehubah tempatan setiap utas tidak disimpan dalam contoh ThreadLocal, tetapi dalam pembolehubah threadLocals bagi utas panggilan. Dalam erti kata lain, pembolehubah tempatan jenis ThreadLocal disimpan dalam ruang memori benang tertentu. ThreadLocal ialah cangkerang alat yang meletakkan nilai ke dalam threadLocals pada utas panggilan melalui kaedah yang ditetapkan dan menyimpannya Apabila utas panggilan memanggil kaedah getnya, ia mengeluarkannya daripada pembolehubah threadLocals bagi utas semasa untuk digunakan. Jika utas panggilan tidak pernah ditamatkan, maka pembolehubah tempatan ini akan sentiasa disimpan dalam pembolehubah threadLocals bagi utas panggilan Oleh itu, apabila pembolehubah tempatan tidak diperlukan, anda boleh memadamkan pembolehubah tempatan daripada threadLocals bagi utas semasa dengan memanggil pembolehubah tempatan. alih keluar kaedah pembolehubah ThreadLocal. Selain itu, mengapakah threadLocals dalam Thread direka sebagai struktur peta? Jelas sekali kerana setiap utas boleh dikaitkan dengan berbilang pembolehubah ThreadLocal. Seterusnya, mari kita lihat kod sumber set ThreadLocal, dapatkan dan alih keluar
public void set(T value) { // 1.获取当前线程(调用者线程) Thread t = Thread.currentThread(); // 2.以当前线程作为key值,去查找对应的线程变量,找到对应的map ThreadLocalMap map = getMap(t); if (map != null) { // 3.如果map不为null,则直接添加元素 map.set(this, value); } else { // 4.否则就先创建map,再添加元素 createMap(t, value); } }
void createMap(Thread t, T firstValue) { /** * 这里是创建一个ThreadLocalMap,以当前调用线程的实例对象为key,初始值为value * 然后放入当前线程的Therad.threadLocals属性里面 */ t.threadLocals = new ThreadLocalMap(this, firstValue); }
ThreadLocalMap getMap(Thread t) { //这里就是直接获取调用线程的成员属性threadlocals return t.threadLocals; }
public T get() { // 1.获取当前线程 Thread t = Thread.currentThread(); // 2.获取当前线程的threadlocals,即ThreadLocalMap ThreadLocalMap map = getMap(t); // 3.如果map不为null,则直接返回对应的值 if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } // 4.否则,则进行初始化 return setInitialValue(); }
Berikut ialah kod setInitialValue
private T setInitialValue() { //初始化属性,其实就是null T value = initialValue(); //获取当前线程 Thread t = Thread.currentThread(); //通过当前线程获取ThreadLocalMap ThreadLocalMap map = getMap(t); //如果map不为null,则直接添加元素 if (map != null) { map.set(this, value); } else { //否则就创建,然后将创建好的map放入当前线程的属性threadlocals createMap(t, value); } //将当前ThreadLocal实例注册进TerminatingThreadLocal类里面 if (this instanceof TerminatingThreadLocal) { TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this); } return value; }
Saya perlu menambah beberapa penjelasan tambahan di siniTerminatingThreadLocal
. Kelas ini baharu dalam jdk11 dan tidak wujud dalam jdk8, jadi tiada perihalan berkaitan kelas ini dalam banyak analisis kod sumber di Internet. Saya telah melihat kod sumber kelas ini, dan fungsinya adalah untuk mengelakkan masalah kebocoran memori ThreadLocal (jika anda berminat, anda boleh menyemak kod sumber, dan sila betulkan saya jika terdapat sebarang ralat). Ini ialah penjelasan rasmi:
/** * A thread-local variable that is notified when a thread terminates and * it has been initialized in the terminating thread (even if it was * initialized with a null value). * 一个线程局部变量, * 当一个线程终止并且它已经在终止线程中被初始化时被通知(即使它被初始化为一个空值)。 */
public void remove() { //如果当前线程的threadLocals 变量不为空, 则删除当前线程中指定ThreadLocal 实例的本地变量。 ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) { m.remove(this); } }
Di dalam setiap utas, terdapat pembolehubah ahli bernama threadLocals, yang jenis Hash Map, dengan kuncinya rujukan ini bagi pembolehubah ThreadLocal yang kami takrifkan, dan nilai ialah nilai yang kami tetapkan menggunakan kaedah yang ditetapkan. Pembolehubah tempatan bagi setiap utas disimpan dalam utas pembolehubah memori sendiri. Jika utas semasa tidak pernah mati, pembolehubah tempatan ini akan sentiasa wujud, jadi ia boleh menyebabkan limpahan memori Oleh itu, ingat untuk memanggil kaedah alih keluar ThreadLocal untuk memadam gunakan. Pembolehubah setempat dalam threadLocals yang sepadan dengan thread.
ThreadLocalMap menggunakan rujukan ThreadLocal yang lemah sebagai kunci Jika ThreadLocal tidak mempunyai rujukan kuat luaran untuk merujuk kepadanya, maka ThreadLocal pasti akan dikitar semula semasa GC sistem , ThreadLocalMap akan Apabila Entri dengan kekunci null muncul , tiada cara untuk mengakses nilai Entri ini dengan kekunci null Jika urutan semasa tidak berakhir untuk masa yang lama, akan sentiasa ada menjadi garis yang kuat untuk nilai Entri ini dengan kunci null: Rujukan Benang -> Sebenarnya, keadaan ini telah diambil kira dalam reka bentuk ThreadLocalMap, dan beberapa langkah perlindungan telah ditambah: semua kekunci dalam thread ThreadLocalMap yang batal akan dikosongkan semasa get(), set(), dan alih keluar () daripada nilai ThreadLocal. Walau bagaimanapun, langkah pencegahan pasif ini tidak dapat menjamin bahawa tiada kebocoran ingatan akan berlaku:
Jika anda menggunakan rujukan yang kukuh: Kami tahu bahawa kitaran hayat ThreadLocalMap pada asasnya adalah sama dengan kitaran hayat Thread Jika utas semasa tidak ditamatkan, maka ThreadLocalMap tidak akan dikitar semula oleh GC , dan ThreadLocalMap memegang hak untuk rujukan ThreadLocal Strong, maka ThreadLocal tidak akan dikitar semula Apabila kitaran hayat benang panjang, jika ia tidak dipadamkan secara manual, ia akan menyebabkan pengumpulan kv, mengakibatkan OOM
ThreadLocal untuk membersihkannya selepas setiap penggunaan. remove()
Atas ialah kandungan terperinci Bagaimana untuk menggunakan kelas Java ThreadLocal. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!