资讯专栏INFORMATION COLUMN

ThreadLocal 解决多线程变量共享问题

gplane / 1697人阅读

摘要:设置当前线程的局部变量副本的变量值为指定值。示例首先,我们来看看不考虑多线程共享数据的情况。使用保存对象的局部变量。各线程间同时操作自己的变量,相互间没有影响。采用以空间换时间方式,为每一个线程都提供一份变量,各线程间同时访问互不影响。

版权声明:本文由吴仙杰创作整理,转载请注明出处:https://segmentfault.com/a/1190000009236777

1. ThreadLocal

ThreadLocal 不是一个线程,而是一个线程的本地化对象。当某个变量在使用 ThreadLocal 进行维护时,ThreadLocal 为使用该变量的每个线程分配了一个独立的变量副本,每个线程可以自行操作自己对应的变量副本,而不会影响其他线程的变量副本。

2. API 方法

ThreadLocal 的 API 提供了如下的 4 个方法。

1)protected T initialValue()

返回当前线程的局部变量副本的变量初始值。

2)T get()

返回当前线程的局部变量副本的变量值,如果此变量副本不存在,则通过 initialValue() 方法创建此副本并返回初始值。

3)void set(T value)

设置当前线程的局部变量副本的变量值为指定值。

4)void remove()

删除当前线程的局部变量副本的变量值。

在实际使用中,我们一般都要重写 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 account = ThreadLocal.withInitial(new Supplier() {
        @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 采用以空间换时间方式,为每一个线程都提供一份变量,各线程间同时访问互不影响。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/69939.html

相关文章

  • 如何看待Spring下单例模式与线程安全的矛盾

    摘要:线程安全问题都是由全局变量及静态变量引起的。常量始终是线程安全的,因为只存在读操作。局部变量是线程安全的。有状态对象,就是有实例变量的对象,可以保存数据,是非线程安全的。 前言 有多少人在使用Spring框架时,很多时候不知道或者忽视了多线程的问题?   因为写程序时,或做单元测试时,很难有机会碰到多线程的问题,因为没有那么容易模拟多线程测试的环境。那么当多个线程调用同一个bean的时...

    dinfer 评论0 收藏0
  • 如何看待Spring下单例模式与线程安全的矛盾

    摘要:线程安全问题都是由全局变量及静态变量引起的。常量始终是线程安全的,因为只存在读操作。局部变量是线程安全的。有状态对象,就是有实例变量的对象,可以保存数据,是非线程安全的。 前言 有多少人在使用Spring框架时,很多时候不知道或者忽视了多线程的问题?   因为写程序时,或做单元测试时,很难有机会碰到多线程的问题,因为没有那么容易模拟多线程测试的环境。那么当多个线程调用同一个bean的时...

    刘永祥 评论0 收藏0
  • Java并发编程之原子性操作

    摘要:将与当前线程建立一对一关系的值移除。为了让方法里的操作具有原子性,也就是在一个线程执行这一系列操作的同时禁止其他线程执行这些操作,提出了锁的概念。 上头一直在说以线程为基础的并发编程的好处了,什么提高处理器利用率啦,简化编程模型啦。但是砖家们还是认为并发编程是程序开发中最不可捉摸、最诡异、最扯犊子、最麻烦、最恶心、最心烦、最容易出错、最不符合社会主义核心价值观的一个部分~ 造成这么多最...

    instein 评论0 收藏0
  • Java 线程(7): ThreadLocal 的应用及原理

    摘要:但是还有另外的功能看的后一半代码作用就是扫描位置之后的数组直到某一个为的位置,清除每个为的,所以使用可以降低内存泄漏的概率。 在涉及到多线程需要共享变量的时候,一般有两种方法:其一就是使用互斥锁,使得在每个时刻只能有一个线程访问该变量,好处就是便于编码(直接使用 synchronized 关键字进行同步访问),缺点在于这增加了线程间的竞争,降低了效率;其二就是使用本文要讲的 Threa...

    shadajin 评论0 收藏0
  • 正确理解ThreadLocal

    摘要:每个线程中都有一个自己的类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确的访问到自己的对象。。但一般来说线程共享的对象通过设置为某类的静态变量就可以实现方便的访问了,似乎没必要放到线程中。 一篇老文章,引用自:http://www.iteye.com/topic/103804 首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过...

    liangzai_cool 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<