ThreadLocal 解決多線程變量共享問題

版權聲明:本文由吳仙傑創做整理,轉載請註明出處:http://www.javashuo.com/article/p-siztiazx-mo.htmljava

1. ThreadLocal

ThreadLocal 不是一個線程,而是一個線程的本地化對象。當某個變量在使用 ThreadLocal 進行維護時,ThreadLocal 爲使用該變量的每一個線程分配了一個獨立的變量副本,每一個線程能夠自行操做本身對應的變量副本,而不會影響其餘線程的變量副本。shell

2. API 方法

ThreadLocal 的 API 提供了以下的 4 個方法。segmentfault

1)protected T initialValue()多線程

返回當前線程的局部變量副本的變量初始值。ide


2)T get()this

返回當前線程的局部變量副本的變量值,若是此變量副本不存在,則經過 initialValue() 方法建立此副本並返回初始值。線程


3)void set(T value)code

設置當前線程的局部變量副本的變量值爲指定值。對象


4)void remove()rem

刪除當前線程的局部變量副本的變量值。


在實際使用中,咱們通常都要重寫 initialValue() 方法,設置一個特定的初始值。

2.1 示例

首先,咱們來看看不考慮多線程共享數據的狀況。

如今有小明、小剛、小紅三人在同一家銀行,分別向各自帳戶存入 200 元錢:

package com.wuxianjiezh.demo.threadpool;

public class MainTest {

    public static void main(String[] args) {
        Bank bank = new Bank();
        Thread xMThread = new Thread(() -> bank.deposit(200), "小明");
        Thread xGThread = new Thread(() -> bank.deposit(200), "小剛");
        Thread xHThread = new Thread(() -> bank.deposit(200), "小紅");
        xMThread.start();
        xGThread.start();
        xHThread.start();
    }
}

class Bank {

    private int money = 1000;

    public void deposit(int money) {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + "--當前帳戶餘額爲:" + this.money);
        this.money += money;
        System.out.println(threadName + "--存入 " + money + " 後帳戶餘額爲:" + this.money);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

運行結果:

小明--當前帳戶餘額爲:1000
小紅--當前帳戶餘額爲:1000
小紅--存入 200 後帳戶餘額爲:1400
小剛--當前帳戶餘額爲:1000
小剛--存入 200 後帳戶餘額爲:1600
小明--存入 200 後帳戶餘額爲:1200

結果是除了小明存錢和本身帳戶餘額能對上外,小剛和小紅也都只存了 200,但他們的帳戶餘額分別多了 200 和 400?

這是由於多個線程共享了同一個實例對象的局部變量所致。


使用 ThreadLocal 保存對象的局部變量。

package com.wuxianjiezh.demo.threadpool;

import java.util.function.Supplier;

public class MainTest {

    public static void main(String[] args) {
        Bank bank = new Bank();
        Thread xMThread = new Thread(() -> bank.deposit(200), "小明");
        Thread xGThread = new Thread(() -> bank.deposit(200), "小剛");
        Thread xHThread = new Thread(() -> bank.deposit(200), "小紅");
        xMThread.start();
        xGThread.start();
        xHThread.start();
    }
}

class Bank {

    // 初始化帳戶餘額爲 100
    ThreadLocal<Integer> account = ThreadLocal.withInitial(new Supplier<Integer>() {
        @Override
        public Integer get() {
            return 1000;
        }
    });

    public void deposit(int money) {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + "--當前帳戶餘額爲:" + account.get());
        account.set(account.get() + money);
        System.out.println(threadName + "--存入 " + money + " 後帳戶餘額爲:" + account.get());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

運行結果:

小明--當前帳戶餘額爲:1000
小紅--當前帳戶餘額爲:1000
小紅--存入 200 後帳戶餘額爲:1200
小剛--當前帳戶餘額爲:1000
小剛--存入 200 後帳戶餘額爲:1200
小明--存入 200 後帳戶餘額爲:1200

能夠看到,咱們要的效果達到了。各線程間同時操做本身的變量,相互間沒有影響。

3. ThreadLocal 與 Thread 同步機制的比較

  • 同步機制採用了以時間換空間方式,經過對象鎖保證在同一個時間,對於同一個實例對象,只有一個線程訪問。

  • ThreadLocal 採用以空間換時間方式,爲每個線程都提供一份變量,各線程間同時訪問互不影響。

相關文章
相關標籤/搜索