Java多线程(十)localthread
来源:互联网 发布:java高级流和低级流 编辑:程序博客网 时间:2024/05/22 07:47
深入研究java.lang.ThreadLocal类
一、概述
ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是threadlocalvariable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是Java中一种较为特殊的线程绑定机制,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。
从线程的角度看,每个线程都保持一个对其线程局部变量副本的隐式引用,只要线程是活动的并且 ThreadLocal 实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。
通过ThreadLocal存取的数据,总是与当前线程相关,也就是说,JVM 为每个运行的线程,绑定了私有的本地实例存取空间,从而为多线程环境常出现的并发访问问题提供了一种隔离机制。
ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单,在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。
概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。
二、API说明
ThreadLocal() 创建一个线程本地变量。
T get() 返回此线程局部变量的当前线程副本中的值,如果这是线程第一次调用该方法,则创建并初始化此副本。
protected T initialValue() 返回此线程局部变量的当前线程的初始值。最多在每次访问线程来获得每个线程局部变量时调用此方法一次,即线程第一次使用 get() 方法访问变量的时候。如果线程先于 get 方法调用 set(T) 方法,则不会在线程中再调用 initialValue 方法。
若该实现只返回 null;如果程序员希望将线程局部变量初始化为 null 以外的某个值,则必须为 ThreadLocal 创建子类,并重写此方法。通常,将使用匿名内部类。initialValue 的典型实现将调用一个适当的构造方法,并返回新构造的对象。
void remove() 移除此线程局部变量的值。这可能有助于减少线程局部变量的存储需求。如果再次访问此线程局部变量,那么在默认情况下它将拥有其 initialValue。
void set(T value) 将此线程局部变量的当前线程副本中的值设置为指定值。许多应用程序不需要这项功能,它们只依赖于 initialValue() 方法来设置线程局部变量的值。
在程序中一般都重写initialValue方法,以给定一个特定的初始值。
三、典型实例
实例一:
public class LocalThreadTest1 implements Runnable {
// 创建线程局部变量studentLocal,在后面你会发现用来保存Student对象
@SuppressWarnings({ "rawtypes", "unused" })
private final static ThreadLocal localThread = new ThreadLocal();// 创建一个线程本地变量
private static AtomicInteger workerCounter = new AtomicInteger();// 计数器
public static void main(String[] agrs) {
LocalThreadTest1 td = new LocalThreadTest1();
Thread t1 = new Thread(td, "线程A");
Thread t2 = new Thread(td, "线程B");
t1.start();
t2.start();
}
@Override
public void run() {
while (true)
try {
Thread.sleep(1000);
accessMethod();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 示例业务方法,用来测试
*/
public void accessMethod() {
// 获取当前线程的名字
String currentThreadName = Thread.currentThread().getName();
System.out.println(currentThreadName + " is running!");
System.out.println(changeStr());
}
@SuppressWarnings("unchecked")
public String changeStr() {
int counter = workerCounter.incrementAndGet();
String x = (String) localThread.get();// // 获取本地线程变量并强制转换为String类型
String y = "";
if (x == null)
x = "第" + counter + "次执行:HELLO";
// 将此线程局部变量的当前线程副本中的值设置为指定值。
localThread.set(x);
if (counter%10 == 10)
localThread.remove();// 移除此线程局部变量的值。
return x;
}
}
// 创建线程局部变量studentLocal,在后面你会发现用来保存Student对象
@SuppressWarnings({ "rawtypes", "unused" })
private final static ThreadLocal localThread = new ThreadLocal();// 创建一个线程本地变量
private static AtomicInteger workerCounter = new AtomicInteger();// 计数器
public static void main(String[] agrs) {
LocalThreadTest1 td = new LocalThreadTest1();
Thread t1 = new Thread(td, "线程A");
Thread t2 = new Thread(td, "线程B");
t1.start();
t2.start();
}
@Override
public void run() {
while (true)
try {
Thread.sleep(1000);
accessMethod();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 示例业务方法,用来测试
*/
public void accessMethod() {
// 获取当前线程的名字
String currentThreadName = Thread.currentThread().getName();
System.out.println(currentThreadName + " is running!");
System.out.println(changeStr());
}
@SuppressWarnings("unchecked")
public String changeStr() {
int counter = workerCounter.incrementAndGet();
String x = (String) localThread.get();// // 获取本地线程变量并强制转换为String类型
String y = "";
if (x == null)
x = "第" + counter + "次执行:HELLO";
// 将此线程局部变量的当前线程副本中的值设置为指定值。
localThread.set(x);
if (counter%10 == 10)
localThread.remove();// 移除此线程局部变量的值。
return x;
}
}
运行结果:
线程A is running!
线程B is running!
第1次执行:HELLO
第2次执行:HELLO
线程B is running!
第1次执行:HELLO
线程A is running!
第2次执行:HELLO
线程A is running!
第2次执行:HELLO
线程B is running!
第1次执行:HELLO
线程B is running!
第1次执行:HELLO
线程A is running!
第2次执行:HELLO
线程B is running!
第1次执行:HELLO
线程A is running!
第2次执行:HELLO
线程B is running!
第1次执行:HELLO
线程A is running!
第2次执行:HELLO
线程B is running!
第1次执行:HELLO
线程A is running!
第2次执行:HELLO
线程B is running!
第1次执行:HELLO
第2次执行:HELLO
线程B is running!
第1次执行:HELLO
线程A is running!
第2次执行:HELLO
线程A is running!
第2次执行:HELLO
线程B is running!
第1次执行:HELLO
线程B is running!
第1次执行:HELLO
线程A is running!
第2次执行:HELLO
线程B is running!
第1次执行:HELLO
线程A is running!
第2次执行:HELLO
线程B is running!
第1次执行:HELLO
线程A is running!
第2次执行:HELLO
线程B is running!
第1次执行:HELLO
线程A is running!
第2次执行:HELLO
实例二:
import java.util.Random;
public class LocalThreadTest implements Runnable {
// 创建线程局部变量studentLocal,在后面你会发现用来保存Student对象
@SuppressWarnings("rawtypes")
private final static ThreadLocal studentLocal = new ThreadLocal();
public static void main(String[] agrs) {
LocalThreadTest td = new LocalThreadTest();
Thread t1 = new Thread(td, "线程A");
Thread t2 = new Thread(td, "线程B");
t1.start();
t2.start();
}
@Override
public void run() {
accessStudent();
}
/**
* 示例业务方法,用来测试
*/
public void accessStudent() {
// 获取当前线程的名字
String currentThreadName = Thread.currentThread().getName();
System.out.println(currentThreadName + " is running!");
// 产生一个随机数并打印
Random random = new Random();
int age = random.nextInt(100);
System.out.println("thread " + currentThreadName + " set age to:" + age);
// 获取一个Student对象,并将随机数年龄插入到对象属性中
Student student = getStudent();
student.setAge(age);
System.out.println("thread " + currentThreadName + " first read age is:" + student.getAge());
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
System.out.println("thread " + currentThreadName + " second read age is:" + student.getAge());
}
@SuppressWarnings("unchecked")
protected Student getStudent() {
// 获取本地线程变量并强制转换为Student类型
Student student = (Student) studentLocal.get();
// 线程首次执行此方法的时候,studentLocal.get()肯定为null
if (student == null) {
// 创建一个Student对象,并保存到本地线程变量studentLocal中
student = new Student();
studentLocal.set(student);
}
return student;
}
}
class Student {
private int age = 0; // 年龄
public int getAge() {
return this.age;
}
public void setAge(int age) {
this.age = age;
}
}
public class LocalThreadTest implements Runnable {
// 创建线程局部变量studentLocal,在后面你会发现用来保存Student对象
@SuppressWarnings("rawtypes")
private final static ThreadLocal studentLocal = new ThreadLocal();
public static void main(String[] agrs) {
LocalThreadTest td = new LocalThreadTest();
Thread t1 = new Thread(td, "线程A");
Thread t2 = new Thread(td, "线程B");
t1.start();
t2.start();
}
@Override
public void run() {
accessStudent();
}
/**
* 示例业务方法,用来测试
*/
public void accessStudent() {
// 获取当前线程的名字
String currentThreadName = Thread.currentThread().getName();
System.out.println(currentThreadName + " is running!");
// 产生一个随机数并打印
Random random = new Random();
int age = random.nextInt(100);
System.out.println("thread " + currentThreadName + " set age to:" + age);
// 获取一个Student对象,并将随机数年龄插入到对象属性中
Student student = getStudent();
student.setAge(age);
System.out.println("thread " + currentThreadName + " first read age is:" + student.getAge());
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
System.out.println("thread " + currentThreadName + " second read age is:" + student.getAge());
}
@SuppressWarnings("unchecked")
protected Student getStudent() {
// 获取本地线程变量并强制转换为Student类型
Student student = (Student) studentLocal.get();
// 线程首次执行此方法的时候,studentLocal.get()肯定为null
if (student == null) {
// 创建一个Student对象,并保存到本地线程变量studentLocal中
student = new Student();
studentLocal.set(student);
}
return student;
}
}
class Student {
private int age = 0; // 年龄
public int getAge() {
return this.age;
}
public void setAge(int age) {
this.age = age;
}
}
运行结果:
线程B is running!
thread 线程B set age to:26
线程A is running!
thread 线程A set age to:53
thread 线程B first read age is:26
thread 线程A first read age is:53
thread 线程A second read age is:53
thread 线程B second read age is:26
thread 线程B set age to:26
线程A is running!
thread 线程A set age to:53
thread 线程B first read age is:26
thread 线程A first read age is:53
thread 线程A second read age is:53
thread 线程B second read age is:26
0 0
- Java多线程(十)localthread
- Java基础<十>--------多线程
- java多线程十 Semaphore
- 【十】Java多线程
- LocalThread分析
- (十)java多线程之CountDownLatch
- Java多线程开发十——杂七杂八
- Java基础第二十一天--多线程2
- 【Java多线程】之十:Timer and TimerTask
- JAVA基础笔记(十)多线程
- (二十)java多线程之ScheduledThreadPoolExecutor
- java多线程学习(十) happen before 原则
- 十、多线程
- java多线程与并发之java并发编程实践(十)
- java基础巩固训练营【第一轮】(十) 多线程
- java基础巩固训练营【第一轮】(十) 多线程 补充
- Java多线程(十)之ReentrantReadWriteLock深入分析
- Java笔记(十)——多线程设计
- 内存池设计与实现
- 【iOS】iOS/Xcode异常:reason:loaded the “SimpleTableViewController” nib but the view outlet was not set
- 自动生成IPv6列表
- Dijstra的greedy简单实现
- (转)《Java虚拟机原理图解》1.4 class文件中的字段表集合--field字段在class文件中是怎样组织的
- Java多线程(十)localthread
- 【iOS】UIImageView点击是现实高亮图,放手时现实默认图
- effective C++ 读书笔记 条款12
- 程序员到三十岁之后需要转行或者转管理吗
- Stanford
- URAL_2025_Line Fighting_均值不等式
- 二分查找
- 最大值和次大值(《编程珠玑(续)》第15章)
- 关于安卓串口编程的详解