selenum破解驗證碼要用到的程式碼
阿新 • • 發佈:2020-10-21
一、ThreadLocal 介紹
1.1 ThreadLocal 是什麼?
ThreadLocal 叫做執行緒變數,在 ThreadLocal 中填充的變數屬於 當前 執行緒,該變數對其他執行緒而言是隔離的。ThreadLocal 為變數在每個執行緒中都建立了一個副本,那麼每個執行緒可以訪問自己內部的副本變數。
1.2 ThreadLocal特性
ThreadLocal 和 Synchronized 都是為了解決多執行緒中相同變數的訪問衝突問題,不同的點是
- Synchronized 通過執行緒等待,犧牲時間來解決訪問衝突
- ThreadLocal 是通過每個執行緒單獨一份儲存空間,犧牲空間來解決衝突,並且相比於 Synchronized
1.3 使用場景
- 在進行物件跨層傳遞的時候,使用ThreadLocal可以避免多次傳遞,打破層次間的約束。
- 執行緒間資料隔離
- 進行事務操作,用於儲存執行緒事務資訊
- 資料庫連線,Session會話管理
1.4 ThreadLocal 方法介紹
// 設定值
void set(T value);
// 獲取值
T get();
// 刪除值
void remove();
// 設定預設預設值
T initialValue();
二、ThreadLocal 的使用
2.1 例項一: ThreadLocal 返回預設值
public static void main(String[] args) { ThreadLocal<Integer> defaultValueLocal = new ThreadLocal() { public Integer initialValue() { return 1; } }; for (int i = 0; i < 10; i++) { Thread thread = new Thread(new Runnable() { @Override public void run() { final Integer result = defaultValueLocal.get(); System.out.println(Thread.currentThread().getName()+ " --> result=" + result); } }); thread.start(); } }
控制檯輸出:
Thread-0 --> result=1
Thread-1 --> result=1
Thread-2 --> result=1
Thread-3 --> result=1
Thread-4 --> result=1
Thread-5 --> result=1
Thread-6 --> result=1
Thread-7 --> result=1
Thread-8 --> result=1
Thread-9 --> result=1
2.2 例項二:set 設定值
public static void main(String[] args) {
ThreadLocal<String> nameLocal = new ThreadLocal<>();
for (int i = 0; i < 10; i++) {
int finalI = i;
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
nameLocal.set(Thread.currentThread().getName() + " -->" + finalI);
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("當前執行緒輸出:" + nameLocal.get());
}
});
thread.start();
}
}
控制檯輸出:
當前執行緒輸出:Thread-2 -->2
當前執行緒輸出:Thread-1 -->1
當前執行緒輸出:Thread-0 -->0
當前執行緒輸出:Thread-3 -->3
當前執行緒輸出:Thread-9 -->9
當前執行緒輸出:Thread-8 -->8
當前執行緒輸出:Thread-7 -->7
當前執行緒輸出:Thread-6 -->6
當前執行緒輸出:Thread-5 -->5
當前執行緒輸出:Thread-4 -->4
三、原始碼
3.1 set 方法
public void set(T value) {
// 獲取當前執行緒物件
Thread t = Thread.currentThread();
// 獲取 ThreadLocalMap 物件
ThreadLocalMap map = getMap(t);
// 判斷 ThreadLocalMap 物件是否存在
if (map != null)
// 存在,則設定值到 ThreadLocalMap 中
map.set(this, value);
else
// 否則建立 ThreadLocalMap 物件
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
**ThreadLocalMap ** 物件是 ThreadLocal 的一個靜態內部類,裡面定義了一個 Entry 來儲存資料,而且還是繼承的弱引用。在 Entry 內部使用 ThreadLocal 作為key,使用我們設定的 value 作為 value:
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
}
3.2 get方法
public T get() {
// 獲取當前執行緒物件
Thread t = Thread.currentThread();
// 獲取 ThreadLocalMap 物件
ThreadLocalMap map = getMap(t);
// 判斷 ThreadLocalMap 物件是否存在
if (map != null) {
// 獲取當前執行緒儲存變數
ThreadLocalMap.Entry e = map.getEntry(this);
// 變數不為空時,將值返回
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
// ThreadLocalMap 物件不存在或不存在指定KEY值時,返回預設值
return setInitialValue();
}
// 設定初始預設值
private T setInitialValue() {
// 呼叫初始預設值設定函式
T value = initialValue();
// 將初始預設值儲存到 ThreadLocalMap 物件中
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
3.3 initialValue 方法
protected T initialValue() {
// 預設返回 null,按需覆蓋
return null;
}
四、ThreadLocal 記憶體洩漏風險
在上面我們介紹 ThreadLocal 原始碼的時候,瞭解到 ThreadLocal 通過 ThreadLocalMap 物件儲存值,它的 key 儲存的是當前的 ThreadLocal 的引用,屬於弱引用。當 ThreadLocal 被回收時,value 卻還存在,就會造成記憶體洩漏。
解決辦法: 在使用完 ThreadLocal 後,執行 remove 操作,避免出現記憶體溢位情況。