java之自己做一个ThreadLocal

直接聊技术!

描述

ThreadLocal直译为 本地线程,但是实际意思却不是这样的,它是一个容器,用于存放本地线程的局部变量,到底为什么叫ThreadLocal,讲道理,我也不知道!


业务描述 在一个类中调用三个线程,三个线程分别进行报数1、2、3

 /**
 * [@author](https://my.oschina.net/arthor):稀饭
 * [@time](https://my.oschina.net/u/126678):下午8:10:59
 * @filename:Sequence.java
 */
package demo;

public interface Sequence {
	public int getNumber();
}
/**
 * [@author](https://my.oschina.net/arthor):稀饭
 * [@time](https://my.oschina.net/u/126678):下午8:12:24
 * @filename:ClientThread.java
 */
package demo;

public class ClientThread extends Thread {

	private Sequence sequence;

	public ClientThread(Sequence sequence) {
		// TODO Auto-generated constructor stub
		this.sequence = sequence;
	}

	/**
	 * @Title: run
	 * @Description: TODO
	 */
	@Override
	public void run() {
		// TODO Auto-generated method stub
		for (int i = 0; i < 3; i++) {
			System.out.println(Thread.currentThread().getName() + " ==> " + sequence.getNumber());
		}
	}
}
/**
 * @author:稀饭
 * @time:下午8:18:27
 * @filename:TestA.java
 */
package demo;

public class TestA implements Sequence {
	private static int number = 0;

	@Override
	public int getNumber() {
		// TODO Auto-generated method stub
		number = number + 1;
		return number;
	}

	public static void main(String[] args) {
		Sequence sequence = new TestA();
		ClientThread clientThread1 = new ClientThread(sequence);
		ClientThread clientThread2 = new ClientThread(sequence);
		ClientThread clientThread3 = new ClientThread(sequence);
		clientThread1.start();
		clientThread2.start();
		clientThread3.start();
	}
}

运行结果如下: Thread-0 ==> 1 Thread-2 ==> 3 Thread-1 ==> 2 Thread-2 ==> 5 Thread-0 ==> 4 Thread-2 ==> 7 Thread-1 ==> 6 Thread-0 ==> 8 Thread-1 ==> 9

**源码分析:**Thread没有按照先后顺序输出,这个可以理解,毕竟线程的启动是随机的,而为什么输出的结果不是1、2、3、1、2、3、1、2、3呢,仔细分析才发现,number是static的,是类所共享的,无法保证对不同线程来说是安全的,大家操作的都是同一个变量,当然一直在递增了。


那么如何做到各自线程递增这样的结果呢?


现在引入ThreadLocal,源码如下 /** * @author:稀饭 * @time:下午8:18:27 * @filename:TestA.java */ package demo;

public class TestB implements Sequence {
	private static ThreadLocal<Integer> container = new ThreadLocal<Integer>() {

		@Override
		protected Integer initialValue() {
			// TODO Auto-generated method stub
			return 0;
		}
	};

	@Override
	public int getNumber() {
		// TODO Auto-generated method stub
		container.set(container.get() + 1);
		return container.get();
	}

	public static void main(String[] args) {
		Sequence sequence = new TestB();
		ClientThread clientThread1 = new ClientThread(sequence);
		ClientThread clientThread2 = new ClientThread(sequence);
		ClientThread clientThread3 = new ClientThread(sequence);
		clientThread1.start();
		clientThread2.start();
		clientThread3.start();
	}
}

运行结果如下: Thread-0 ==> 1 Thread-1 ==> 1 Thread-2 ==> 1 Thread-1 ==> 2 Thread-0 ==> 2 Thread-1 ==> 3 Thread-2 ==> 2 Thread-0 ==> 3 Thread-2 ==> 3

源码解析:在TestA中引入ThreadLocal之后输出的结果变成了我上面说的那样,用了ThreadLocal之后每个线程独立了,虽然同样是static,但是线程独立了,也就是说ThreadLocal会为每一个不同的线程设置一个独立的副本。


现在才是最重要的地方,我们自己来实现一个ThreadLocal

先来分析一下ThreadLocal的api:

 public void set(T vlue) 将值放入线程局部变量中

 public T get() 从线程局部变量中获取

 public void remove() 从线程局部中移除值

 protected T initialValue() 返回线程局部变量中的初始值

以下是我自己实现的ThreadLocal


/**
 * @author:稀饭
 * @time:上午10:36:34
 * @filename:ThreadLocalContainer.java
 */
package demo;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class ThreadLocalContainer<T> {

	private T value;
	//这里使用了同步Map
	private Map<String, T> map = Collections.synchronizedMap(new HashMap<String, T>());

	// 将值放入线程局部变量中
	public void set(T vlue) {
		map.put(Thread.currentThread().getName(), vlue);
	}

	// 从线程局部变量中获取
	public T get() {
		if (!map.containsKey(Thread.currentThread().getName())&&map.get(Thread.currentThread().getName()) == null) {
			value = initialValue();
			map.put(Thread.currentThread().getName(), value);
		}
		T v = map.get(Thread.currentThread().getName());
		return v;
	}

	// 从线程局部中移除值
	public void remove() {
		if (map.containsKey(Thread.currentThread().getName())) {
			map.remove(Thread.currentThread().getName());
		}
	}

	// 返回线程局部变量中的初始值
	protected T initialValue() {
		return value;
	}
}

**源码分析:**在源码中我使用了线程安全的Map也就是同步Map,目的是为了防止出现多线程出现的不安全问题。


**Note:发布的这些文章全都是自己边学边总结的,难免有纰漏,如果发现有不足的地方,希望可以指出来,一起学习咯,么么哒。 开源爱好者,相信开源的力量必将改变世界: **  osc    : https://git.oschina.net/xi_fan github: https://github.com/wiatingpub

转载于:https://my.oschina.net/u/2286602/blog/833941

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值