threadlocal.get取值为null
时间: 2024-04-08 12:32:31 浏览: 409
`ThreadLocal.get()` 方法返回当前线程中与此 `ThreadLocal` 关联的值。如果返回 `null`,则表示当前线程尚未设置该 `ThreadLocal` 的值。
可能导致 `ThreadLocal.get()` 返回 `null` 的原因有以下几种:
1. 未设置初始值:如果在访问 `ThreadLocal` 之前没有调用 `ThreadLocal.set()` 方法设置初始值,那么 `ThreadLocal.get()` 将返回 `null`。
2. 值被移除或清除:在多线程环境中,其他线程可能会调用 `ThreadLocal.remove()` 或 `ThreadLocal.set(null)` 来清除或移除与该 `ThreadLocal` 关联的值。这可能导致在当前线程中调用 `ThreadLocal.get()` 返回 `null`。
3. 线程间传递问题:`ThreadLocal` 的值是线程私有的,不同线程之间无法共享。如果你在一个线程中设置了 `ThreadLocal` 的值,然后在另一个线程中调用 `ThreadLocal.get()`,将会返回 `null`。
请检查上述情况,确定是哪种情况导致了 `ThreadLocal.get()` 返回 `null`。
相关问题
threadlocal取值
在使用threadlocal时,可以通过调用get()方法来获取当前线程的变量值。例如:
```
ThreadLocal<String> tl = new ThreadLocal<>();
tl.set("hello world");
String value = tl.get(); // 获取当前线程的变量值
```
如果在一个非当前线程中调用get()方法,则会返回null。因此,在使用ThreadLocal时,需要注意确保在正确的线程中获取变量值。
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } 分析这两段代码
<think>我们重点分析Android中ThreadLocal<Looper>的初始化过程及其线程唯一性检查机制。结合引用内容,具体展开如下:
---
### 一、ThreadLocal<Looper>的初始化流程
#### 1. **Looper.prepare() 触发初始化**
- 开发者必须显式调用 `Looper.prepare()` 为当前线程创建Looper(主线程由系统自动创建)[^1][^2]。
- 初始化逻辑:
```java
public static void prepare() {
if (sThreadLocal.get() != null) { // 检查是否已存在Looper
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper()); // 创建并存储新Looper
}
```
- **关键点**:
- `sThreadLocal` 是 `static final ThreadLocal<Looper>` 类型[^1][^2]。
- 通过 `sThreadLocal.set()` 将新Looper绑定到**当前线程的ThreadLocalMap**中。
#### 2. **Looper构造过程**
- 在 `new Looper()` 时完成自身初始化:
```java
private Looper() {
mQueue = new MessageQueue(); // 创建消息队列
mThread = Thread.currentThread(); // 记录所属线程
}
```
- 此时 `mThread` 被赋值为当前线程对象(如主线程或子线程)[^1]。
---
### 二、线程唯一性检查机制
#### 1. **检查逻辑**
- 在 `prepare()` 中通过 `sThreadLocal.get() != null` 判断:
- 若已存在Looper,则抛出异常 `Only one Looper may be created per thread`[^1][^2]。
- **原理**:
- `ThreadLocal` 的存储基于线程隔离的 `ThreadLocalMap`,每个线程独立存储自己的Looper副本[^4][^5]。
- 当线程首次调用 `prepare()` 时,`sThreadLocal.get()` 返回 `null`;后续调用则返回已创建的Looper。
#### 2. **ThreadLocal的底层支持**
- **存储结构**:
- 每个线程 `Thread` 内部维护一个 `ThreadLocalMap` 对象[^5]。
- `ThreadLocalMap` 的键(Key)是 `ThreadLocal` 实例(如 `sThreadLocal`),值(Value)是Looper对象。
- **取值过程**(以 `myLooper()` 为例):
```java
public static @Nullable Looper myLooper() {
return sThreadLocal.get(); // 从当前线程的Map中获取Looper
}
```
- 内部通过当前线程的 `ThreadLocalMap`,用 `sThreadLocal` 作为Key查找Looper[^4][^5]。
---
### 三、线程检查的应用场景
#### 1. **判断当前线程类型**
- **UI线程检查**:通过比较当前线程Looper与主线程Looper[^1]:
```java
if (Looper.getMainLooper() == Looper.myLooper()) {
// 当前是主线程
} else {
// 子线程
}
```
- **实现原理**:
- `Looper.getMainLooper()` 返回主线程的Looper(系统初始化时固定)。
- `Looper.myLooper()` 返回当前线程的Looper(通过 `sThreadLocal.get()` 动态获取)。
#### 2. **Android视图刷线程安全**
- 在 `ViewRootImpl` 中,通过比较 `mThread`(构造时记录的线程)和 `Thread.currentThread()` 确保UI操作在主线程执行[^1]:
```java
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException();
}
}
```
- 此处的 `mThread` 即 `Looper` 初始化时记录的线程对象。
---
### 四、ThreadLocal的线程隔离性验证
引用[3]的代码演示了 `ThreadLocal` 的线程隔离特性:
```java
private ThreadLocal<Boolean> booleanThreadLocal = new ThreadLocal<>();
// 主线程设置 true
booleanThreadLocal.set(true);
// 子线程1设置 false
new Thread(() -> booleanThreadLocal.set(false)).start();
// 子线程2未设置,返回 null
new Thread(() -> System.out.println(booleanThreadLocal.get())).start();
```
输出:
```
main---true
thread1--false
thread2--null
```
**结论**:每个线程对 `ThreadLocal` 的读写互不影响,验证了Looper的线程绑定机制。
---
**总结**:
- **初始化**:`Looper.prepare()` 通过 `ThreadLocal.set()` 将Looper绑定到当前线程的 `ThreadLocalMap`。
- **唯一性**:`prepare()` 中的 `sThreadLocal.get() != null` 检查确保每线程最多一个Looper。
- **线程检查**:`myLooper()` 和 `getMainLooper()` 配合实现线程类型判断,保障Android UI线程安全。
§§相关问题§§
1. `ThreadLocalMap` 如何处理内存泄漏问题?
2. Android 主线程 Looper 是如何初始化的?
3. 为什么子线程默认没有 Looper?
4. `ThreadLocal` 在哪些其他框架中有类似应用?</think>### 分析 Android 中 `ThreadLocal<Looper>` 的初始化及线程唯一性检查机制
#### 一、ThreadLocal 存储 Looper 的核心机制
1. **线程隔离存储原理**
- `ThreadLocal<Looper>` 本质是**线程局部变量**,每个线程独立存储 Looper 副本
- 实现结构:
```java
public final class Looper {
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>();
// 其他成员...
}
```
- 存储逻辑:
```mermaid
graph LR
Thread1(Thread A) --> MapA(ThreadLocalMap)
Thread2(Thread B) --> MapB(ThreadLocalMap)
MapA --> Entry1(Entry: key=sThreadLocal, value=Looper_A)
MapB --> Entry2(Entry: key=sThreadLocal, value=Looper_B)
```
2. **初始化流程**
关键方法 `prepare()` 实现线程唯一性初始化:
```java
public static void prepare() {
if (sThreadLocal.get() != null) { // 唯一性检查
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper()); // 创建绑定当前线程的Looper
}
```
- **检查机制**:通过 `sThreadLocal.get()` 检测当前线程是否已有 Looper
- **异常触发**:重复初始化时抛出 `RuntimeException`(如子线程多次调用 `prepare()`)
#### 二、线程检查机制实现
1. **主线程检测原理**
- 通过比较当前线程 Looper 与主线程 Looper:
```java
public static boolean isMainThread() {
return getMainLooper().getThread() == Thread.currentThread();
}
```
- 典型应用(UI 线程检查):
```java
void invalidateView() {
if (Looper.getMainLooper() == Looper.myLooper()) {
invalidate(); // 主线程直接刷新
} else {
postInvalidate(); // 子线程通过消息队列刷新
}
}
```
2. **Looper 获取的线程关联性**
- `myLooper()` 实现:
```java
public static @Nullable Looper myLooper() {
return sThreadLocal.get(); // 从当前线程的ThreadLocalMap获取
}
```
- 获取流程:
```mermaid
sequenceDiagram
participant App as 应用代码
participant TL as ThreadLocal
participant TM as ThreadLocalMap
App->>TL: myLooper()
TL->>TM: get(当前线程)
TM-->>TL: 返回Looper对象
TL-->>App: Looper
```
#### 三、线程唯一性保障设计
1. **数据结构层面**
- `ThreadLocalMap` 使用**弱引用键**(避免内存泄漏):
```java
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k); // 弱引用持有ThreadLocal
value = v; // 强引用持有Looper
}
}
```
- 键值关系:`ThreadLocal 实例 → Looper 对象`
2. **设计约束**
| 约束类型 | 实现方式 | 目的 |
|------------------|-----------------------------------|--------------------------|
| **线程唯一性** | `prepare()` 中的 null 检查 | 防止单线程创建多个 Looper |
| **线程隔离性** | ThreadLocalMap 存储 | 确保各线程 Looper 独立 |
| **主线程标识** | `getMainLooper()` 返回固定 Looper | 提供主线程判断基准 |
#### 四、未初始化场景处理
1. **子线程默认无 Looper**
- 子线程直接调用 `Looper.myLooper()` 返回 `null`
- 典型异常:在未调用 `prepare()` 的子线程创建 Handler 会崩溃:
```
Can't create handler inside thread that has not called Looper.prepare()
```
2. **安全检测最佳实践**
```java
public void safeUIUpdate() {
if (Looper.myLooper() != null
&& Looper.myLooper() == Looper.getMainLooper()) {
updateUI(); // 确认在主线程执行
}
}
```
**总结**:
Android 通过 `ThreadLocal<Looper>` 实现线程绑定的消息循环机制,其核心是通过:
1. **线程局部存储**:确保每个线程独立持有 Looper 实例
2. **初始化检查**:`prepare()` 中的 `sThreadLocal.get() != null` 保障线程唯一性
3. **主线程标识**:`getMainLooper()` 提供全局主线程基准
该设计是 Android 消息机制(Handler/Looper)的基石,保障了 UI 线程安全性[^1][^2][^5]。
阅读全文
相关推荐


















