您的位置:首页 > 其它

ThreadLocal原理与应用详解(1)

2017-08-29 11:23 330 查看
本文介绍一下Android中(其实也就是Java中)线程本地变量的相关知识和使用方法。

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的第一个好处:隔离解耦。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: