ThreadLocalはスレッドクロージャーの重要な手段であり、SpringのDateTimeContextHolderとRequestContextHolderも役立ちます。
ThreadLocalの使用シナリオ
シナリオ1:オブジェクトの分離-スレッドには排他的なオブジェクト(SimpleDateFormatなど)が必要です
スレッドは、通常、スレッドの安全性と効率性の理由から、RandomやDateFormatなどのオブジェクトツールクラスを排他的に共有します。
スレッド専用オブジェクトのシナリオの場合、主なメソッドはinnitialValue()メソッドをオーバーライドすることです。
public class RrightWaySimpleDateFormater {
public static ExecutorService threadPool = Executors.newFixedThreadPool(10);
/**
* 定义ThreadLocal变量--JDK8实现形式
*/
private static final ThreadLocal<SimpleDateFormat> dateThreadSafe = ThreadLocal.withInitial(
() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
);
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 1000; i++) {
int finalI = i;
threadPool.submit(() -> {
System.out.println(new RrightWaySimpleDateFormater().date(finalI));
});
}
threadPool.shutdown();
}
public String date(int seconds) {
return dateThreadSafe.get().format(new Date(1000 * seconds));
}
}
JDK7および以前の実装フォーム
private static final ThreadLocal<SimpleDateFormat> dateThreadSafe2 = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
};
ThreadLocalは通常、スレッドプールには適していないことに注意してください。
Ali javaマニュアル:
[必須] SimpleDateFormatはスレッドセーフでないクラスであり、通常は静的変数として定義しないでください。静的として定義されている場合は、ロックするか、DateUtilsツールクラスを使用する必要があります。上記の治療もお勧めします。
JDK8では、SimpleDateFormatの代わりにDateTimeFormatter、Dateの代わりにInstant、Calendarの代わりにLocalDateTimeを使用することをお勧めします。
シナリオ2:オブジェクト転送
スレッドはグローバル変数を保存して、データをパラメーターとしてレイヤーごとに渡す必要なしに、さまざまなメソッドを直接使用できるようにする必要があります。
ThreadLocalを使用して、ユーザー権限情報、ユーザー名、userId、携帯電話番号などの一部のビジネス情報を保存できます。ビジネス情報は同じスレッドでも同じですが、異なるスレッドのコンテンツは異なります。同じリクエスト内(同じスレッド内)の異なるメソッド間で共有することに重点が置かれています。
マップには、上記のビジネス情報を保存することもできます。複数のスレッドが同時に動作する場合は、スレッドの安全性を保証する必要があります。たとえば、静的ConcurrentHashMap変数を使用し、スレッドIDをキーとして使用し、ビジネスデータを値として格納すると、スレッド間の分離を実現できますが、パフォーマンスに影響を与えます。
public class RightWayThreadLocalUse {
public static void main(String[] args) {
new ServiceImpl1().service(new UserInfo("lzp", "1234567890"));
}
}
/**
* 接收数据
*/
class ServiceImpl1 {
public void service(UserInfo userInfo) {
UserInfoHolder.holder.set(userInfo);
new ServiceImpl2().service();
}
}
/**
* 处理数据1
*/
class ServiceImpl2 {
public void service() {
System.out.println("客户名:" + UserInfoHolder.holder.get().cltNam);
new ServiceImpl3().service();
}
}
/**
* 处理数据2
*/
class ServiceImpl3 {
public void service() {
System.out.println("客户号:" + UserInfoHolder.holder.get().cltNbr);
// 此时使用完ThreadLocal,回收该ThreadLocal
UserInfoHolder.holder.remove();
}
}
class UserInfoHolder {
public static ThreadLocal<UserInfo> holder = new ThreadLocal<>();
}
class UserInfo {
String cltNam;
String cltNbr;
public UserInfo(String cltNam, String cltNbr) {
this.cltNam = cltNam;
this.cltNbr = cltNbr;
}
}
ThreadLocalの利点
(1)ThreadLocal変数はスレッド専用であるため、スレッドは安全でロックする必要がないため、ブロッキングが発生せず、実行効率が向上します。
(2)メモリを効率的に使用し、オーバーヘッドを節約します。スレッドプールを使用するシナリオでは、各ワーカースレッドがオブジェクトのThreadLocalインスタンスを持っている必要があるだけです。
(3)コールチェーンのシナリオでは、ビジネス情報の格納に使用されるオブジェクトの繰り返し送信を避け、コードの分離を実現します。