线程局部变量ThreadLocal

我们知道在线程间共享变量会存在风险,即线程安全问题,解决线程安全问题的办法有很多种,之前讲到的同步机制就是一种。

其实我们也可以通过使用ThreadLocal辅助类为各个线程提供各自的实例,避免共享变量。

同步机制是采用“以时间换空间”,而线程局部变量则是采用“以空间换时间”。

因为同步机制是将线程的执行由“并行执行”转为串行执行,没有获得锁的线程需要等待获得锁的线程释放锁,增加了时间上的开销;线程局部变量由于每个线程都有变量的副本,对自己变量副本的修改不会影响其他线程,所以可以独立执行,不需要考虑到其他线程,时间上的开销减少了,但是每个线程都拥有变量的副本,这无疑又增加了空间上的开销。

使用方法

ThreadLocal不是一个线程,只是线程的一个本地化对象,ThreadLocal实例通常是类中的private static字段。

要为每个线程构造一个实例,可以使用以下代码:

public static ThreadLocal<Integer> ThreadNum = new ThreadLocal<Integer>() {
		protected Integer initialValue() {
			return 0;
		}
	};

以上代码是采用匿名内部类的方式构造了一个ThreadLocal类,并且我们应该覆盖initialValue方法来提供一个初始值,若不重写此方法,则,默认返回null。

ThreadLocal类除了initialValue方法外,还有set、get和remove方法。

在这里插入图片描述

下面我们写一个完整的程序来体会一下ThreadLocal的使用:

public class ThreadLocalTest {
	public static void main(String[] args) {
		Thread MyThread1 = new Thread(new MyThread(), "MyThread1");
		Thread MyThread2 = new Thread(new MyThread(), "MyThread2");

		MyThread1.start();
		MyThread2.start();
	}

}

class MyThread implements Runnable {

	public static ThreadLocal<Integer> ThreadNum = new ThreadLocal<Integer>() {
		protected Integer initialValue() {
			return 0;
		}
	};

	@Override
	public void run() {
		// TODO Auto-generated method stub
		for (int i = 0; i < 10; i++) {
			ThreadNum.set(i);
			System.out.println(Thread.currentThread().getName() + "->" + ThreadNum.get());
		}

	}

}

运行结果:

在这里插入图片描述

可以看到,Thread1和Thread2两个线程都各自打印了0~9十个数字,没有互相干扰。

源码分析

ThreadLocal是一个以ThreadLocal对象为键、任意对象为值的存储结构,并且是根据特定的线程去取值。

在ThreadLocal类中有一个静态内部类ThreadLocalMap,它可以理解为就是一个Map,存储以ThreadLocal对象为key,线程局部变量为值的value的Entry对象。
在这里插入图片描述

再看一下get方法的源码,我们会发现,其中的逻辑是先获取到当前的线程对象,再通过当前线程对象来获得他的ThreadLocalMap对象。如果Map不为空,则以当前ThreadLocal对象为key去获取Entry中对应的value值。如果Map为空,则会调用setInitialValue()方法,将初始值存入Map中。

在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/is_Javaer/article/details/88377598