Rumah  >  Artikel  >  Java  >  Bagaimana untuk menggunakan kelas Java ThreadLocal

Bagaimana untuk menggunakan kelas Java ThreadLocal

王林
王林ke hadapan
2023-05-14 18:49:061092semak imbas

    Seperti yang ditunjukkan dalam gambar:

    Bagaimana untuk menggunakan kelas Java ThreadLocal

    Permulaan Pantas

    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:

    Bagaimana untuk menggunakan kelas Java ThreadLocal

    Prinsip ThreadLocal

    Rajah kelas berkaitan ThreadLocal

    Mari kita lihat dahulu struktur gambar rajah kelas kelas berkaitan ThreadLocal, sebagai ditunjukkan dalam rajah:

    Bagaimana untuk menggunakan kelas Java ThreadLocal

    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

    set

        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;
        }

    get

        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).
     * 一个线程局部变量,
     * 当一个线程终止并且它已经在终止线程中被初始化时被通知(即使它被初始化为一个空值)。
     */

    alih keluar

         public void remove() {
             //如果当前线程的threadLocals 变量不为空, 则删除当前线程中指定ThreadLocal 实例的本地变量。
             ThreadLocalMap m = getMap(Thread.currentThread());
             if (m != null) {
                 m.remove(this);
             }
         }

    Ringkasan

    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.

    Kebocoran memori ThreadLocal

    Mengapa kebocoran memori berlaku?

    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:

    • Menggunakan ThreadLocal statik memanjangkan kitaran hayat ThreadLocal, yang boleh menyebabkan kebocoran memori

    • Peruntukan menggunakan ThreadLocal dan tidak lagi memanggil kaedah get(), set(), remove(), yang akan membawa kepada kebocoran memori

    Mengapa menggunakan rujukan yang lemah?

    Memandangkan kita semua tahu bahawa menggunakan rujukan yang lemah akan menyebabkan kebocoran memori ThreadLocalMap, mengapakah pegawai masih menggunakan rujukan yang lemah dan bukannya rujukan yang kuat? Ini bermula dengan perbezaan antara menggunakan rujukan lemah dan rujukan kuat:

    • 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

    • Jika anda menggunakan rujukan lemah: lemah Objek dalam rujukan mempunyai tempoh pengisytiharan yang sangat singkat, kerana semasa sistem GC, selagi rujukan lemah ditemui, objek akan dikitar semula tanpa mengira sama ada ruang timbunan berada mencukupi. Apabila rujukan kukuh ThreadLocal dikitar semula, rujukan lemah yang dipegang oleh ThreadLocalMap juga akan dikitar semula Jika kv tidak dipadamkan secara manual, ia akan menyebabkan pengumpulan nilai dan juga membawa kepada OOM

    Dari. perbandingan, kita dapat melihat bahawa menggunakan rujukan yang lemah sekurang-kurangnya dapat memastikan bahawa OOM tidak akan disebabkan oleh pengumpulan kunci peta, dan nilai yang sepadan boleh dikosongkan pada panggilan seterusnya melalui kaedah alih keluar, dapatkan dan tetapkan. Ia boleh dilihat bahawa punca kebocoran memori bukanlah rujukan yang lemah, tetapi kitaran hayat ThreadLocalMap adalah sepanjang Thread, menyebabkan pengumpulan

    Penyelesaian

    Memandangkan punca masalah adalah pengumpulan nilai yang menyebabkan OOM, Kemudian kami mengambil ubat yang betul dan hanya memanggil kaedah

    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!

    Kenyataan:
    Artikel ini dikembalikan pada:yisu.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam