ThreadLocal(一)设计ThreadLocal的目的

Posted zhangjin1120

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ThreadLocal(一)设计ThreadLocal的目的相关的知识,希望对你有一定的参考价值。

ThreadLocal(一)设计ThreadLocal的目的
ThreadLoca(二)Looper中ThreadLocal的使用



先看看ThreadLocal类的文档注释:

/**
 * This class provides thread-local variables.  These variables differ from
 * their normal counterparts in that each thread that accesses one (via its
 * @code get or @code set method) has its own, independently initialized
 * copy of the variable.  @code ThreadLocal instances are typically private
 * static fields in classes that wish to associate state with a thread (e.g.,
 * a user ID or Transaction ID).
 *
 * <p>For example, the class below generates unique identifiers local to each
 * thread.
 * A thread's id is assigned the first time it invokes @code ThreadId.get()
 * and remains unchanged on subsequent calls.
 * <pre>
 * import java.util.concurrent.atomic.AtomicInteger;
 *
 * public class ThreadId 
 *     // Atomic integer containing the next thread ID to be assigned
 *     private static final AtomicInteger nextId = new AtomicInteger(0);
 *
 *     // Thread local variable containing each thread's ID
 *     private static final ThreadLocal&lt;Integer&gt; threadId =
 *         new ThreadLocal&lt;Integer&gt;() 
 *             &#64;Override protected Integer initialValue() 
 *                 return nextId.getAndIncrement();
 *         
 *     ;
 *
 *     // Returns the current thread's unique ID, assigning it if necessary
 *     public static int get() 
 *         return threadId.get();
 *     
 * 
 * </pre>
 * <p>Each thread holds an implicit reference to its copy of a thread-local
 * variable as long as the thread is alive and the @code ThreadLocal
 * instance is accessible; after a thread goes away, all of its copies of
 * thread-local instances are subject to garbage collection (unless other
 * references to these copies exist).
 *

翻译过来:

此类提供线程本地变量。这些变量不同于线程的普通对应变量,因为访问一个变量的每个线程(通过其get或set方法)都有自己的、独立初始化的变量副本。ThreadLocal实例通常是类中的private static 字段,这个字段希望将状态与线程(例如,用户ID或事务ID)关联的类中。
 例如,下面的类为每个线程生成本地的唯一标识符。
一个线程的id, 在第一次调用时被分配,在随后的调用中保持不变。


 import java.util.concurrent.atomic.AtomicInteger;

 public class ThreadId 
     // Atomic integer containing the next thread ID to be assigned
     private static final AtomicInteger nextId = new AtomicInteger(0);

     // Thread local variable containing each thread's ID
     private static final ThreadLocal<Integer> threadId =
         new ThreadLocal<Integer>() 
             @Override protected Integer initialValue() 
                 return nextId.getAndIncrement();
         
     ;

     // Returns the current thread's unique ID, assigning it if necessary
     public static int get() 
         return threadId.get();
     
 
 
 

只要线程处于活动状态且ThreadLocal实例可访问,每个线程都持有对其线程局部变量副本的隐式引用;线程消失后,其线程本地实例的所有副本都将接受垃圾收集(除非存在对这些副本的其他引用)。

java程序中,某个线程的非私有成员变量是可以被其他线程访问和修改的。如下:

public class ThreadTest 
    public static void main(String[] args) 
        ThreadB b = new ThreadB();
        new Thread(new Runnable() 
            @Override
            public void run() 
                b.publicName ="pubThreadBBB";
                b.protectedName = "proThreadBBB";
                b.defaultName = "defThreadBBB";
                System.out.println("将 ThreadB的名字修改为:" + b.defaultName);
            
        ).start();
    

//新建一个ThreadB.java文件
public class ThreadB extends Thread 
    public String publicName = "ThreadB";
    protected String protectedName = "ThreadB";
    String defaultName = "ThreadB";
    private String privateName = "ThreadB";

如果想要某个线程私有的变量,不能被其他线程访问和修改,怎么办?

用private修饰或者设为默认,不就好了。
还真就是这样,我们看看Thread的源码:

public class Thread implements Runnable 
	...
    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;
    ...

注释翻译过来就是:与此线程相关的ThreadLocal值。此映射由ThreadLocal类维护。所以:
设计ThreadLocal的目的,是为了存储线程私有的变量,其他线程无法访问,也就是实现了线程封闭。

如何防止其他线程,修改线程本地变量?

将ThreadB的写法,修改如下:

public class ThreadB extends Thread 
    //ThreadLocal的注释中,建议用private static修饰
    private static ThreadLocal<String> tl = new ThreadLocal<>(); 

    public ThreadB(String name) 
        super();
        tl.set(name);
    

    public String getNam() 
        return tl.get(); //ThreadLocal内部有判空处理
    

这样,其他线程,就访问不能修改name变量了。

如果线程私有的变量有许多,其是不同类型,怎么办?

把多个变量,写在一个类里面,再把这个类作为ThreadLocal的泛型。例如:android SDK中,Looper内部包含了MessageQueue,MessageQueue就是线程私有的。

以上是关于ThreadLocal(一)设计ThreadLocal的目的的主要内容,如果未能解决你的问题,请参考以下文章

ThreadLocal模式的核心元素

用了三年 ThreadLocal 今天才弄明白其中的道理

用了三年 ThreadLocal 今天才弄明白其中的道理

广州某小公司:ThreadLocal面试

Java并发:ThreadLocal的简单介绍

java题库总结3