ThreadLocal简介

ThreadLocal

一. 概述


ThreadLocal(是Thread Local Variable,线程局部变量)类是Java为线程安全提供的一个工具类,代表一个线程局部变量。把数据放在ThreadLocal中可以让每个线程创建一个该变量的副本,线程间可以独立地改变自己的副本,而不会和其他线程产生副本冲突,从而避免并发访问的线程安全问题,就像每个线程都完全拥有该变量一样。

方法:

  • T get() 返回此线程局部变量的当前线程副本值
  • void remove() 删除此线程局部变量的当前线程的副本值
  • void set(T value) 设置此线程局部变量中当前线程副本值

二. 案例


package myThread;

/**
 * 若进程间不需要数据共享,则可使用此方法
 * 避免再去设计安全同步
 */
public class ThreadLocalTest {
    public static void main(String[] args) {
        Account acc = new Account("初始名");
        /*
        虽然两个线程共享一个账户,但该账户名是ThreadLocal类型
        每个线程拥有自己的副本,互不影响
         */
        new MyTest(acc,"线程甲").start();
        new MyTest(acc,"线程乙").start();
    }
}

class Account{
    /*
    定义一个ThreadLocal类型的线程局部变量(伪装成变量的对象),
     每个线程保存一个该变量的副本。
     */
    private ThreadLocal<String> name=new ThreadLocal<String>();

    public Account(String name){
        this.name.set(name);
        System.out.println("---"+this.name.get());
    }
    public String getName(){
        return this.name.get();
    }
    public void setName(String str) {
        this.name.set(str);
    }
}

class MyTest extends Thread{
    //定义一个Account的成员变量
    private Account account;
    public MyTest(Account account,String name){
        super(name);
        this.account=account;
    }

    /**
     * 进入run后,线程局部变量会被copy为默认值
     * string->null
     */
    public void run(){
        for (int i = 0; i < 10; i++) {
            if(i==6){
                /*
                此处的getName()是Thread的静态方法
                用线程名改变了原先的副本值(null)
                 */
                account.setName(getName());
            }
            System.out.println(account.getName()+"账户的i值:"+i);
        }
    }
}

三. 总结


ThreadLocal和其他所有同步机制一样,都是为了解决多线程中对同一变量的访问冲突。不同的是,在普通同步机制中,是通过对象加锁来实现安全访问的,而ThreadLocal从另一个角度来解决多线程的并发访问。简单说ThreadLocal就是一种以空间换时间的做法,在每个Thread里面维护了一个以开地址法实现的ThreadLocal.ThreadLocalMap,把数据进行隔离,数据不共享,自然就没有线程安全方面的问题了。

两者面向问题的领域不同,不能互相代替。
 

问: ThreadLocal需要注意什么?

使用ThreadLocal要注意remove!

ThreadLocal 的 实 现 是 基 于 一 个 所 谓 的 ThreadLocalMap,在 ThreadLocalMap中 , 它的 key 是 一 个 弱引用。通常 弱引用都会和引用队列配合清理机制使用,但是ThreadLocal 是个例外 ,它并没有这么做。这意味着 ,废弃项目的回收依 赖于显式地触发,否则就要等待线程结束 ,进而回收相应ThreadLocalMap!这就是很多OOM(内存溢出) 的来源 ,所以通常都会建议应用一定要自己负责remove,并且不要和线程池配合 ,因为worker线程往往是不会退出的。

posted @ 2020-07-12 18:08  仰观云  阅读(232)  评论(0编辑  收藏  举报
Live2D