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;
}
}


运行结果:
线程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

实例二:
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;
}
}

运行结果:

线程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


0 0
原创粉丝点击