java多线程ThreadLocal作用理解,自己写一个ThreadLocal线程容器

    java多线程ThreadLocal作用理解,自己写一个ThreadLocal线程容器

 

一、什么是 ThreadLocal

1、ThreadLocal 直接翻译为:“线程本地” 或 “本地线程”,如果真的这么认为,那就错了!其实他是一个容器,用于存放线程局部变量,在多线程情况下,确保各个线程变量相互独立,互不干扰。

 

二、通过代码来理解

1.1、创建一个接口 Sequence

/**
* description: 定义一个 Sequence 接口
* @version v1.0
* @author w
* @date 2018年12月1日下午4:04:53
**/
public interface Sequence {
    int getNum();
}

 

1.2、创建一个ClientThread 类 继承Thread ,输出线程编号和number 结果

/**
* description: ClientThread 输出线程编号和 number 结果
* @version v1.0
* @author w
* @date 2018年12月1日下午4:23:46
**/
public class ClientThread extends Thread {

    private Sequence sequence;

    public ClientThread(Sequence sequence) {
        this.sequence = sequence;
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " ---> " +sequence.getNum());
        }
    }
}

 

2.1、创建一个 SequenceA 类,实现 Sequence 接口

/**
* description: SequenceA 实现 Sequence 接口
* @version v1.0
* @author w
* @date 2018年12月1日下午4:05:58
**/
public class SequenceA implements Sequence {
    // private int num = 0 ; 不添加 static,结果也是一样
    private static int num = 0 ;

    @Override
    public int getNum() {
        num = num + 1;
        return num;
    }

    public static void main(String[] args) {
        Sequence sequence = new SequenceA();

        // 创建3个线程对象
        Thread t1 = new ClientThread(sequence);
        Thread t2 = new ClientThread(sequence);
        Thread t3 = new ClientThread(sequence);

        // 启动三个线程
        t1.start();
        t2.start();
        t3.start();
    }
}

 

2.2、输出结果如下 (多线程情况下,每次执行结果,可能都不一样):

Thread-1 ---> 1
Thread-0 ---> 1
Thread-0 ---> 2
Thread-0 ---> 3
Thread-0 ---> 4
Thread-0 ---> 5
Thread-1 ---> 6
Thread-1 ---> 7
Thread-1 ---> 8
Thread-1 ---> 9
Thread-2 ---> 10
Thread-2 ---> 11
Thread-2 ---> 12
Thread-2 ---> 13
Thread-2 ---> 14

 

2.3、结论: 根据结果可以看到,线程变量被共享了。 要想每个线程独立输出1-5, 就需要用到存放线程变量的容器 ---> ThreadLocal 。

 

3.1、创建 SequenceB 类实现 Sequence 接口,用ThreadLocal 作为存放线程变量的容器

/**
 * description: SequenceB 实现 Sequence 接口,且使用 ThreadLocal 作为存放每个线程变量的容器
 * @version v1.0
 * @author w
 * @date 2018年12月1日 16:14:12
 **/
public class SequenceB implements Sequence  {
	private static final ThreadLocal numContainer = new ThreadLocal(){
		@Override
		protected Integer initialValue() {
			return 0;
		};
	};

	@Override
	public int getNum() {
		Integer num = numContainer.get();
		num = num +1 ;
		numContainer.set(num);
		return num;
	}
	
	public static void main(String[] args) {
		// 创建sequence 对象
		Sequence sequence = new SequenceB();
		// 创建3个线程对象
		Thread s1 = new ClientThread(sequence);
		Thread s2 = new ClientThread(sequence);
		Thread s3 = new ClientThread(sequence);
		
		// 启动3个线程
		s1.start();
		s2.start();
		s3.start();
	}
}

 

3.2、输出结果如下:

Thread-1 ---> 1
Thread-0 ---> 1
Thread-2 ---> 1
Thread-0 ---> 2
Thread-1 ---> 2
Thread-0 ---> 3
Thread-2 ---> 2
Thread-0 ---> 4
Thread-0 ---> 5
Thread-1 ---> 3
Thread-2 ---> 3
Thread-2 ---> 4
Thread-2 ---> 5
Thread-1 ---> 4
Thread-1 ---> 5

 

3.3、结论:使用 ThreadLocal 作为存放线程变量的容器,实现了每个线程变量独立输出1-5的需求,解决了多线程情况下,变量共享的问题。

 

三、ThreadLocal 类主要方法

1、protected T initialValue() : 初始化变量值 。(默认:null)
2、public T get() : 从线程局部变量中,获取值。
3、public void set(T value) :将值放入线程局部变量中。
4、public void remove(): 从线程局部变量中移除值。(有助于JVM垃圾回收!)
5、补充: initialValue 方法修饰符是 protected ,提醒大家要给线程局部变量设置一个初始值。

 

四、仿照 ThreadLocal 写一个功能类似的 MyThreadLocal

1、创建一个 MyThreadLocal 类,山寨一个 java.lang.ThreadLocal

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
 * description: 自己写一个 java.lang.ThreadLocal ,仿制的山寨版
 * @version v1.0
 * @author w
 * @date 2018年12月1日下午4:37:57
 **/
public class MyThreadLocal {	
	private Map container = Collections.synchronizedMap(new HashMap());
	
	protected T initVariable(){
		return null;
	}
	
	public T get(){
		Thread currentThread = Thread.currentThread();
		T value = container.get(currentThread);
		if(null == value && !container.containsKey(currentThread)){
			value = initVariable();
			container.put(currentThread, value);
		}
		return value ;
	}
	
	public void set(T value){
		container.put(Thread.currentThread(), value);
	}
	
	public void remove(){
		container.remove(Thread.currentThread());
	}
}

 

2.1、创建 SequenceC类,使用 MyThreadLocal 作为存放线程变量的容器

/**
 * description: 使用 自定义的 MyThreadLocal 实现 ThreadLocal功能,线程变量独立,互不影响。
 * @version v1.0
 * @author w
 * @date 2018年12月1日下午4:48:05
 **/
public class SequenceC implements Sequence {
	
	private static MyThreadLocal container = new MyThreadLocal(){
		@Override
		protected Integer initVariable() {
			return 0 ;
		};
	};
	
	@Override
	public int getNum() {
		Integer num = container.get();
		num = num + 1; 
		container.set(num);
		return num;
	}
	
	public static void main(String[] args) {
		Sequence sequence = new SequenceC();
		
		Thread t1 = new ClientThread(sequence);
		Thread t2 = new ClientThread(sequence);
		Thread t3 = new ClientThread(sequence);
		
		t1.start();
		t2.start();
		t3.start();
	}
}

2.2、输出结果如下:

Thread-1 ---> 1
Thread-1 ---> 2
Thread-0 ---> 1
Thread-2 ---> 1
Thread-0 ---> 2
Thread-1 ---> 3
Thread-0 ---> 3
Thread-2 ---> 2
Thread-0 ---> 4
Thread-1 ---> 4
Thread-1 ---> 5
Thread-0 ---> 5
Thread-2 ---> 3
Thread-2 ---> 4
Thread-2 ---> 5

 

2.3、结论:使用 MyThreadLocal 实现了 ThreadLocal 的功能,保证了线程变量不共享,相互独立。

 

五、总结

1、ThreadLocal 可以理解为是一个存放局部线程变量的容器。 将当前线程和需要存放的变量,做一个映射,从而保证各个线程互不干扰。

2、作用简单记为: 多线程情况下,保证各个线程变量之间相互独立,互不干扰!

 

 

参考资料: 《架构探险:从零开始写Java Web框架》 --- page 169 。

         ThreadLocal-面试必问深度解析

 

 

你可能感兴趣的:(Java)