欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java之ThreadLocal使用常見和方式案例講解

 更新時間:2021年08月09日 11:26:55   作者:mind_programmonkey  
這篇文章主要介紹了Java之ThreadLocal使用常見和方式案例講解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下

【面試高頻】- ThreadLocal的使用場景以及使用方式是怎么樣的

1 兩大使用場景-ThreadLocal的用途

  • 典型場景1:每個線程需要一個獨享的對象(通常是工具類,典型需要使用的類有SimpleDateFormat和Random)
  • 典型場景2:每個線程內(nèi)需要保存全局變量(例如在攔截器中獲取用戶信息),可以讓不同方法直接使用,避免參數(shù)傳遞的麻煩。

2 典型場景1:每個線程需要一個獨享的對象

每個Thread內(nèi)有自己的實例副本,不共享;

舉例:SimpleDateFormat。(當多個線程共用這樣一個SimpleDateFormat,但是這個類是不安全的)

  • 2個線程分別用自己的SimpleDateFormat,這沒問題;
  • 后來延伸出10個,那就有10個線程和10個SimpleDateFormat,這雖然寫法不優(yōu)雅,但勉強可以接受
  • 但是當需求變成了1000,那么必然要用線程池,消耗內(nèi)存太多;
  • 但是每一個SimpleDateFormat我們都需要創(chuàng)建一遍,那么太耗費new對象了,改成static共用的,所有線程都共用一個simpleDateFormat對象,但這是線程不安全的,容易出現(xiàn)時間一致的情況,在調(diào)用的時候,可加鎖來解決,但還是不優(yōu)雅;
  • 用ThreadLocal來解決該問題,給每個線程分配一個simpledateformat,可這個threadlocal是安全的;
package threadlocal;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 描述:     利用ThreadLocal,給每個線程分配自己的dateFormat對象,保證了線程安全,高效利用內(nèi)存
 */
public class ThreadLocalNormalUsage05 {

    public static ExecutorService threadPool = Executors.newFixedThreadPool(10);

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 1000; i++) {
            int finalI = i;
            threadPool.submit(new Runnable() {
                @Override
                public void run() {
                    String date = new ThreadLocalNormalUsage05().date(finalI);
                    System.out.println(date);
                }
            });
        }
        threadPool.shutdown();
    }

    public String date(int seconds) {
        //參數(shù)的單位是毫秒,從1970.1.1 00:00:00 GMT計時
        Date date = new Date(1000 * seconds);
//        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        SimpleDateFormat dateFormat = ThreadSafeFormatter.dateFormatThreadLocal2.get();
        return dateFormat.format(date);
    }
}

class ThreadSafeFormatter {

    public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        }
    };

    public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal2 = ThreadLocal
            .withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
}

3 典型場景2:當前用戶信息需要被線程內(nèi)所有方法共享

  • 一個比較繁瑣的解決方案是把user作為參數(shù)層層傳遞,從service-1()傳到service-2(),以此類推,但是這樣做會導(dǎo)致代碼冗余且不易維護。
  • 進階點就是userMap來保存,但是當多線程同時工作時,需要保證線程安全,需要用synchronized,或者concurrenthashmap,但無論用什么,都會對性能有所影響

每個線程內(nèi)需要保存全局變量,可以讓不同方法直接使用,避免參數(shù)傳遞的麻煩

  • 用ThreadLocal保存一些業(yè)務(wù)內(nèi)存(用戶權(quán)限信息,從用戶系統(tǒng)獲取到的用戶名、userId等)
  • 這些信息在同一個線程內(nèi)相同,但是不同的線程使用的業(yè)務(wù)內(nèi)容是不相同的
  • 在線程生命周期內(nèi),都通過這個靜態(tài)ThreadLocal實例的get()方法取得自己set過的那個對象,避免了將這個對象作為參數(shù)傳遞的麻煩
package threadlocal;

/**
 * 描述:     演示ThreadLocal用法2:避免傳遞參數(shù)的麻煩
 */
public class ThreadLocalNormalUsage06 {

    public static void main(String[] args) {
        new Service1().process("");

    }
}

class Service1 {

    public void process(String name) {
        User user = new User("超哥");
        UserContextHolder.holder.set(user);
        new Service2().process();
    }
}

class Service2 {

    public void process() {
        User user = UserContextHolder.holder.get();
        ThreadSafeFormatter.dateFormatThreadLocal.get();
        System.out.println("Service2拿到用戶名:" + user.name);
        new Service3().process();
    }
}

class Service3 {

    public void process() {
        User user = UserContextHolder.holder.get();
        System.out.println("Service3拿到用戶名:" + user.name);
        UserContextHolder.holder.remove();
    }
}

class UserContextHolder {

    public static ThreadLocal<User> holder = new ThreadLocal<>();


}

class User {

    String name;

    public User(String name) {
        this.name = name;
    }
}

注意點:

  • 強調(diào)的是同一個請求內(nèi)(同一個線程內(nèi))不同方法見的共享;
  • 不需重寫initialValue()方法,但是必須手動調(diào)用set()方法

4 ThreadLocal方法使用總結(jié)

場景一:initialValue

在ThreadLocal第一次get的時候把對象給初始化出來,對象的初始化時機可以由我們控制。

場景二:set

如果需要保存到ThreadLocal里面的對象的生成時機不由我們隨意控制。例如攔截器生成的用戶信息,用ThreadLocal.set直接放到ThreadLocal當中。

5 ThreadLocal原理

理清Thread,ThreadLocalMap以及ThreadLocal

在這里插入圖片描述

主要方法介紹

  • T initialValue(): 初始化
  • void set(T t): 為這個線程設(shè)置一新值
  • T get(): 得到這個線程對應(yīng)的value。如果是首次調(diào)用get()。則會調(diào)用initialize來得到這個值
  • void remove(): 刪除這個線程得到的值

ThreadLocalMap發(fā)生沖突之后,會用線性探測法。

6 ThreadLocal使用問題內(nèi)存泄露

什么是內(nèi)存泄露:某個對象不再有用,但是占用的內(nèi)存卻不能被回收。

Value的泄露

  • 在ThreadLocalMap中的每個Entry都是一個對key的弱引用,同時,每個Entry都包含了一個對value的強引用。
  • 正常情況 ,當線程終止,保存在ThreadLocal里的value會被垃圾回收,因為沒有任何強引用了。
  • 但是,如果線程不終止(比如線程池需要保持很久),那么key對應(yīng)的value就不能被回收。Thread->ThreadLocalMap->Entry(key為Null)->Value
  • 因為value和Thread之間還存在這個強引用鏈路,所以導(dǎo)致value無法回收,就可能出現(xiàn)OOM;JDK已經(jīng)考慮到這個問題,所以在set,remove,rehash方法中會掃描key為null,會把value也設(shè)置為null,這樣value對象就可以被回收了。
  • 但是如果一個ThreadLocal不被使用,那么實際上set,remove,rehash方法也不會被調(diào)用,如果同時線程又不停止,那么調(diào)用鏈就一直存在,那么就導(dǎo)致了value的內(nèi)存泄露。

如何避免內(nèi)存泄露呢?

  • 調(diào)用remove方法,就會刪除對應(yīng)的Entry對象,可以避免內(nèi)存泄露,所以使用完ThreadLocal之后,應(yīng)該調(diào)用remove方法。

7 實際應(yīng)用場景-在spring中的實例分析

  • DateTimeContextHolder:用到了ThreadLocal
  • RequestContextHolder:用到了ThreadLocal

到此這篇關(guān)于Java之ThreadLocal使用常見和方式案例講解的文章就介紹到這了,更多相關(guān)Java之ThreadLocal使用常見和方式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring?Boot?整合RocketMq實現(xiàn)消息過濾功能

    Spring?Boot?整合RocketMq實現(xiàn)消息過濾功能

    這篇文章主要介紹了Spring?Boot?整合RocketMq實現(xiàn)消息過濾,本文講解了RocketMQ實現(xiàn)消息過濾,針對不同的業(yè)務(wù)場景選擇合適的方案即可,需要的朋友可以參考下
    2022-06-06
  • java得到某年某周的第一天實現(xiàn)思路及代碼

    java得到某年某周的第一天實現(xiàn)思路及代碼

    某年某周的第一天,此功能如何使用java編程得到呢?既然有了問題就有解決方法,感興趣的朋友可以了解下本文,或許會給你帶來意想不到的收獲哦
    2013-01-01
  • 淺談SpringBoot主流讀取配置文件三種方式

    淺談SpringBoot主流讀取配置文件三種方式

    這篇文章主要介紹了淺談SpringBoot主流讀取配置文件三種方式,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習或者工作具有一定的參考學(xué)習價值,需要的朋友們下面隨著小編來一起學(xué)習學(xué)習吧
    2020-09-09
  • java實現(xiàn)基于TCP協(xié)議網(wǎng)絡(luò)socket編程(C/S通信)

    java實現(xiàn)基于TCP協(xié)議網(wǎng)絡(luò)socket編程(C/S通信)

    這篇文章主要介紹了java實現(xiàn)基于TCP協(xié)議網(wǎng)絡(luò)socket編程(C/S通信),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習或者工作具有一定的參考學(xué)習價值,需要的朋友們下面隨著小編來一起學(xué)習學(xué)習吧
    2020-10-10
  • OGNL表達式基本語法與用法詳解

    OGNL表達式基本語法與用法詳解

    這篇文章主要介紹了OGNL表達式基本語法與用法詳解,具有一定參考價值。需要的朋友可以了解下。
    2017-09-09
  • Spring中@Repository注解的作用和用法以及和@Mapper的區(qū)別詳析

    Spring中@Repository注解的作用和用法以及和@Mapper的區(qū)別詳析

    這篇文章主要給大家介紹了關(guān)于Spring中@Repository注解的作用和用法以及和@Mapper的區(qū)別的相關(guān)資料,注解的作用是標識一個類為數(shù)據(jù)訪問對象,并由Spring框架進行實例化和管理,需要的朋友可以參考下
    2023-09-09
  • Maven3種打包方式中maven-assembly-plugin的使用詳解

    Maven3種打包方式中maven-assembly-plugin的使用詳解

    這篇文章主要介紹了Maven3種打包方式中maven-assembly-plugin的使用,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-07-07
  • Spring中SmartLifecycle和Lifecycle的作用和區(qū)別

    Spring中SmartLifecycle和Lifecycle的作用和區(qū)別

    這篇文章主要介紹了Spring中SmartLifecycle和Lifecycle的作用和區(qū)別,本文通過實例代碼給大家介紹的非常詳細對大家的學(xué)習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-03-03
  • 深入淺析Java 循環(huán)中標簽的作用

    深入淺析Java 循環(huán)中標簽的作用

    這篇文章主要介紹了深入淺析Java 循環(huán)中標簽的作用的相關(guān)資料,需要的朋友可以參考下
    2016-02-02
  • 基于Scala和Java方法的相互調(diào)用

    基于Scala和Java方法的相互調(diào)用

    這篇文章主要介紹了Scala和Java方法的相互調(diào)用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11

最新評論