天天看点

Java多线程(十)localthread

深入研究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