1. 程式人生 > 其它 >6.22 Java多執行緒ThreadLocal

6.22 Java多執行緒ThreadLocal

6.22 Java多執行緒ThreadLocal

執行緒變數的特點

  • 多執行緒環境下,每個執行緒都有自己的資料

  • 一個執行緒的區域性變數只有自己能看見,不會影響其他執行緒(使用區域性變數比使用其他變數好)

ThreadLocal的特點

  • ThreadLocal能夠放一個執行緒級別的變數

  • 本身能夠被多個執行緒共享使用,又能達到執行緒安全的目的

  • 在多執行緒環境下保證成員變數的安全

常用方法:

get/set/initialValue(初始化)方法

JDK建議ThreadLocal定義為private static

ThreadLocal常用的地方(內部儲存結構類似於Map)

  • 為每一個執行緒繫結一個數據庫連線(每一個執行緒有一個數據庫連線)、HTTP請求、使用者身份資訊等

    • Hibernate的Session 工具類HibernateUtil

    • 通過不同的執行緒物件設定Bean屬性,保證各個執行緒Bean物件的獨立性

  • 每個執行緒擁有一個ThreadLocal

    • ThreadLocal內部的儲存結構類似於Map

      • key是執行緒資訊

      • value是對應的儲存內容

      • 這樣可以達到每個執行緒的資料相互獨立,又可以共享一塊大的區域。達到在多執行緒環境下保證成員變數的安全。

ThreadLocal的例項demo
package thread.rearrangement;

/**
* ThreadLocal:每個執行緒自身的儲存區域(區域性)
* get/set/initialValue
* @since JDK 1.8
* @date 2021/6/22
* @author Lucifer
*/
public class ThreadLocalTestNo1 {

/*宣告類屬性*/
// private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();

/*更改初始值--->建立threadlocal的子類,重寫initialValue*/
// private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>(){
// protected Integer initialValue(){
// return 200; //初始化的值就更改成了200
// };
// };

/*lambda表示式更改初始值*/
// private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> {
// return 200;
// });

/*更加簡化程式碼*/
private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 200);

/*
這樣就開闢了一個大的儲存空間
每來一個執行緒都會自動開闢一個區域
這些開闢的空間都會在大的儲存空間內
*/

public static void main(String[] args) {
System.out.println(Thread.currentThread().getName() + "--->" + threadLocal.get());
/*因為是整數,所以預設值是Null,不是零*/

/*設定值*/
threadLocal.set(99);
System.out.println(Thread.currentThread().getName() + "--->" + threadLocal.get());

/*
現在所有的儲存區域屬於主執行緒main
*/

/*例項化執行緒內部類*/
new Thread(new MyRun()).start();
}

/**
* 寫一個內部類,實現Runnable介面
* 執行緒類
*/
public static class MyRun implements Runnable{

/*重寫run方法*/
@Override
public void run(){

/*設定個隨機數值*/
threadLocal.set((int)(Math.random() * 99));

/*獲取值*/
System.out.println(Thread.currentThread().getName() + "--->" + threadLocal.get());

}
}
}
/*
每一個執行緒擁有自己的一個大的空間,裡面儲存的是自己的資料,大家相互不影響
*/
ThreadLocal修改屬性變數的例項demo
package thread.rearrangement;

/**
* 每個執行緒儲存自己的資料,更改不會影響其他執行緒
* @since JDK 1.8
* @date 2021/6/23
* @author Lucifer
*/
public class ThreadLocalTestNo2 {

/*更加簡化程式碼*/
private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 1);

/*
這樣就開闢了一個大的儲存空間
每來一個執行緒都會自動開闢一個區域
這些開闢的空間都會在大的儲存空間內
*/

public static void main(String[] args) {
for (int i=0; i<5; i++){
new Thread(new MyRun()).start();
}
}

/**
* 寫一個內部類,實現Runnable介面
* 執行緒類
*/
public static class MyRun implements Runnable{

/*重寫run方法*/
@Override
public void run(){

/*獲取當前執行緒的值*/
Integer left = threadLocal.get();
/*
get方法的作用:
返回當前執行緒的此執行緒區域性變數的副本中的值
*/

/*獲取值*/
System.out.println(Thread.currentThread().getName() + "得到了--->" + left);

/*操作資源數*/
threadLocal.set(left-1);

/*再次列印*/
System.out.println(Thread.currentThread().getName() + "還剩下--->" + threadLocal.get());

}
}
}
ThreadLocal呼叫分析
package thread.rearrangement;

/**
* 分析ThreadLocal環境
* @since JDk 1.8
* @date 2021/6/23
* @author Lucifer
*/
public class ThreadLocalTestNo3 {

/*建立ThreadLocal儲存空間*/
private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 1);

public static void main(String[] args) {
new Thread(new MyRun()).start();
new Thread(new MyRun()).start();
}

/**
* 建立一個內部執行緒類
*/
public static class MyRun implements Runnable{

/*寫一個構造器*/
public MyRun(){
threadLocal.set(-100);
System.out.println(Thread.currentThread().getName() + "--->" + threadLocal.get());
}

/*重寫Runnable介面的run方法*/
@Override
public void run(){
System.out.println(Thread.currentThread().getName() + "--->" + threadLocal.get());
}
}
}
InheritableThreadLocal拷貝執行緒資料例項
package thread.rearrangement;

/**
* InheritableThreadLocal:繼承上下文、環境的資料、起點
* @since JDK 1.8
* @date 2021/6/23
* @author Lucifer
*/
public class ThreadLocalTestNo4 {

/*開闢ThreadLocal執行緒空間*/
private static InheritableThreadLocal<Integer> threadLocal = new InheritableThreadLocal<>();

public static void main(String[] args) {

/*設定值*/
threadLocal.set(2);

/*執行緒屬性變數*/
System.out.println(Thread.currentThread().getName() + "--->" + threadLocal.get());

/*修改拷貝的屬性*/
threadLocal.set(200);
/*
這樣修改以後上面的執行緒就是初始設定的2
下面的新的執行緒為現在設定的200
*/

/*新建一個執行緒體*/
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "--->" + threadLocal.get());
}).start();
/*
這是新的執行緒體的,他的值是null
如果將ThreadLocal改為InheritableThreadLocal那麼set值就會被延續到下一個執行緒體當中
因為新建的執行緒由main執行緒開闢,所以會將資料拷貝一份給子執行緒
這是拷貝不是共享,所以新的執行緒體還可以對屬性進行修改
*/
}
}