1. 程式人生 > 實用技巧 >selenum破解驗證碼要用到的程式碼

selenum破解驗證碼要用到的程式碼

一、ThreadLocal 介紹

1.1 ThreadLocal 是什麼?

ThreadLocal 叫做執行緒變數,在 ThreadLocal 中填充的變數屬於 當前 執行緒,該變數對其他執行緒而言是隔離的。ThreadLocal 為變數在每個執行緒中都建立了一個副本,那麼每個執行緒可以訪問自己內部的副本變數。

1.2 ThreadLocal特性

ThreadLocalSynchronized 都是為了解決多執行緒中相同變數的訪問衝突問題,不同的點是

  • Synchronized 通過執行緒等待,犧牲時間來解決訪問衝突
  • ThreadLocal 是通過每個執行緒單獨一份儲存空間,犧牲空間來解決衝突,並且相比於 Synchronized
    ThreadLocal 具有執行緒隔離的效果,只有在執行緒內才能獲取到對應的值,執行緒外則不能訪問到想要的值。

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 操作,避免出現記憶體溢位情況。