ThreadLocal原理与应用详解(1)
2017-08-29 11:23
330 查看
本文介绍一下Android中(其实也就是Java中)线程本地变量的相关知识和使用方法。
This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. 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).
For example, the class below generates unique identifiers local to each thread. A thread’s id is assigned the first time it invokes ThreadId.get() and remains unchanged on subsequent calls.
Each thread holds an implicit reference to its copy of a thread-local variable as long as the thread is alive and the 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).
有几个关键信息:
(1)ThreadLocal是线程独有的。
(2)ThreadLocal的生命周期与线程绑定。
(3)ThreadLocal支持泛型。
(4)在形式上可以使用为静态变量,在Java框架层面实现线程独有拷贝。
(5)实现了延迟初始化。
官方文档给出了一个例子,线程ID:
想象一下ThreadId这个工具类需要具备的能力:首先需要有一个全局的且线程安全的计数变量,用来作为自增线程ID值生成的依据;第二,需要为每个线程生成一个ID值,其使用是以线程为单位。
以此分析上述例程。第一点是通过java.util.concurrent.atomic.AtomicInteger对象来实现线程安全,其操作是原子的:https://developer.android.com/reference/java/util/concurrent/atomic/AtomicInteger.html
An int value that may be updated atomically. See the java.util.concurrent.atomic package specification for description of the properties of atomic variables. An AtomicInteger is used in applications such as atomically incremented counters, and cannot be used as a replacement for an Integer. However, this class does extend Number to allow uniform access by tools and utilities that deal with numerically-based classes.
int getAndIncrement ()
Atomically increments by one the current value.
再看第二点。我们知道,线程ID是每一个线程所独有独占,一旦分配其值不可改变。那么,如果不使用ThreadLocal,其他的解决方案?恐怕只有在Thread对象上做文章了。譬如我们写一个Thread的子类:
第一种:同样实现延迟初始化,使用双锁,并使用local变量int id间接访问volatile int myThreadId,尽可能提升性能
第二种:不实现延迟初始化,因为nextId已经具有操作原子性,且在构造方法中初始化,所以不需要再在赋值逻辑额外增加线程安全机制
上述这两种方式与使用ThreadLocal相比,显然不如ThreadLocal方案漂亮。ThreadLocal方案使得Thread对外不可见,完全隔离了,将相关业务逻辑完全解耦放到了一个ThreadId类中,程序员不需要趟Thread的混水了。
当然,这里仅仅是以Thread ID作为一个例子。真实的Java Thread ID是类似第二种的实现方式。以Java Thread的一种构造方法为例:
到现在,我们可以看到使用ThreadLocal的第一个好处:隔离解耦。
1.从Android官方文档开始
Android官方文档相关介绍并不多,几分钟就看完了:https://developer.android.com/reference/java/lang/ThreadLocal.html :This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. 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).
For example, the class below generates unique identifiers local to each thread. A thread’s id is assigned the first time it invokes ThreadId.get() and remains unchanged on subsequent calls.
Each thread holds an implicit reference to its copy of a thread-local variable as long as the thread is alive and the 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).
有几个关键信息:
(1)ThreadLocal是线程独有的。
(2)ThreadLocal的生命周期与线程绑定。
(3)ThreadLocal支持泛型。
(4)在形式上可以使用为静态变量,在Java框架层面实现线程独有拷贝。
(5)实现了延迟初始化。
官方文档给出了一个例子,线程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(); } }
想象一下ThreadId这个工具类需要具备的能力:首先需要有一个全局的且线程安全的计数变量,用来作为自增线程ID值生成的依据;第二,需要为每个线程生成一个ID值,其使用是以线程为单位。
以此分析上述例程。第一点是通过java.util.concurrent.atomic.AtomicInteger对象来实现线程安全,其操作是原子的:https://developer.android.com/reference/java/util/concurrent/atomic/AtomicInteger.html
An int value that may be updated atomically. See the java.util.concurrent.atomic package specification for description of the properties of atomic variables. An AtomicInteger is used in applications such as atomically incremented counters, and cannot be used as a replacement for an Integer. However, this class does extend Number to allow uniform access by tools and utilities that deal with numerically-based classes.
int getAndIncrement ()
Atomically increments by one the current value.
再看第二点。我们知道,线程ID是每一个线程所独有独占,一旦分配其值不可改变。那么,如果不使用ThreadLocal,其他的解决方案?恐怕只有在Thread对象上做文章了。譬如我们写一个Thread的子类:
第一种:同样实现延迟初始化,使用双锁,并使用local变量int id间接访问volatile int myThreadId,尽可能提升性能
import java.util.concurrent.atomic.AtomicInteger; public class MyThread extends Thread { private static final AtomicInteger nextId = new AtomicInteger(0); private volatile int myThreadId = -1; public int getMyThreadId() { int id = myThreadId; if (id == -1) { synchronized (this) { id = myThreadId; if (id == -1) { id = nextId.getAndIncrement(); myThreadId = id; } } } return myThreadId; } }
第二种:不实现延迟初始化,因为nextId已经具有操作原子性,且在构造方法中初始化,所以不需要再在赋值逻辑额外增加线程安全机制
import java.util.concurrent.atomic.AtomicInteger; public class MyThread extends Thread { private static final AtomicInteger nextId = new AtomicInteger(0); private int myThreadId; public MyThread() { myThreadId = nextId.getAndIncrement(); } public int getMyThreadId() { return myThreadId; } }
上述这两种方式与使用ThreadLocal相比,显然不如ThreadLocal方案漂亮。ThreadLocal方案使得Thread对外不可见,完全隔离了,将相关业务逻辑完全解耦放到了一个ThreadId类中,程序员不需要趟Thread的混水了。
当然,这里仅仅是以Thread ID作为一个例子。真实的Java Thread ID是类似第二种的实现方式。以Java Thread的一种构造方法为例:
private static int count = 0; /** * Package-scope method invoked by Dalvik VM to create "internal" * threads or attach threads created externally. * * Don't call Thread.currentThread(), since there may not be such * a thing (e.g. for Main). */ Thread(ThreadGroup group, String name, int priority, boolean daemon) { synchronized (Thread.class) { id = ++Thread.count; } // 省略无关代码 } /** * Returns the thread's identifier. The ID is a positive <code>long</code> * generated on thread creation, is unique to the thread, and doesn't change * during the lifetime of the thread; the ID may be reused after the thread * has been terminated. * * @return the thread's ID. */ public long getId() { return id; }
到现在,我们可以看到使用ThreadLocal的第一个好处:隔离解耦。
相关文章推荐
- ThreadLocal原理与应用详解(2)
- ICCV2017 | 一文详解GAN之父Ian Goodfellow 演讲《生成对抗网络的原理与应用》(附完整PPT)
- MPU6050原理详解及实例应用
- ThreadLocal原理与应用
- ThreadLocal的原理和在框架中的应用
- 详解defer和async的原理及应用
- Struts应用详解:三大组成部分及工作原理
- ThreadLocal原理及其实际应用
- 多线程原理实例应用详解
- java高级进阶关于java多线程的应用 ThreadLocal多线程实例详解
- Android四大组件之服务-Service 原理和应用开发详解
- java高级进阶关于java多线程的应用 ThreadLocal多线程实例详解
- ASP.NT运行原理和页面生命周期详解及其应用
- Storm概念、原理详解及其应用(一)BaseStorm
- SURF算法应用工业检测之二(原理详解)
- 【Android应用开发】 推送原理解析 极光推送使用详解 (零基础精通推送)
- java多线程模式ThreadLocal原理简述及其使用详解
- java多线程模式ThreadLocal原理简述及其使用详解
- 栈原理详解及其应用实现