文档章节

java之自己做一个ThreadLocal

李锡钒
 李锡钒
发布于 2017/02/08 12:09
字数 955
阅读 18
收藏 0

直接聊技术!

描述

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

© 著作权归作者所有

李锡钒
粉丝 0
博文 4
码字总数 4500
作品 0
汕尾
程序员
私信 提问
eclipse中远程调试问题

eclipse 远程调试总是报 eclipse中总是报 Failed to connect to remote VM. Connection timed out. org.eclipse.jdi.TimeoutException at org.eclipse.jdi.internal.connect.SocketTransport......

liuhaiyong
2014/06/24
4.9K
4
Java虚拟机调优 JVM优化 工作非常实用的调优

JVM大体结构 class 文件 加载-验证-准备-解析-初始化-使用-卸载 一个线程一个栈,一个方法一个栈帧 如何确定垃圾 + 引用计数 会有循环引用的问题 + 正向可达 从roots对象计数可以达到的对象 ...

张泽立
04/28
128
0
设计模式(思想)还是基础?

最近在网上看到一个提问:j2se的基础重要,还是设计模式、设计思想重要? 近似永远正确的回答是:两者都重要。 解道banq的观点: 我不想说太多,我可以告诉你我目前的情况: 我已经几年没有去...

穿越星辰
2010/05/13
57
0
如果你想学习Java,那么就来看这篇文章

一、前言 我是从大二开始学习的Java,当时的目标是Java Web开发,当时并不想考研,所以当时的学习是以就业为主,现在我大三了,学习Java Web开发已经一年了,因为种种原因,决定要考研,所以...

Jivanmoon
2018/08/27
0
0
SpringBoot+MyBatis+MySQL读写分离

引言 读写分离要做的事情就是对于一条SQL该选择哪个数据库去执行,至于谁来做选择数据库这件事儿,无非两个,要么中间件帮我们做,要么程序自己做。因此,一般来讲,读写分离有两种实现方式。...

编程SHA
01/14
84
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周六乱弹 —— 如果是个帅小伙你愿意和他出去吗

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 小小编辑推荐:《Ghost 》游戏《死亡搁浅》原声 《Ghost 》游戏(《死亡搁浅》原声) - Au/Ra / Alan Walker 手机党少年们想听歌,请使劲儿戳...

小小编辑
36分钟前
56
5
java通过ServerSocket与Socket实现通信

首先说一下ServerSocket与Socket. 1.ServerSocket ServerSocket是用来监听客户端Socket连接的类,如果没有连接会一直处于等待状态. ServetSocket有三个构造方法: (1) ServerSocket(int port);...

Blueeeeeee
今天
6
0
用 Sphinx 搭建博客时,如何自定义插件?

之前有不少同学看过我的个人博客(http://python-online.cn),也根据我写的教程完成了自己个人站点的搭建。 点此:使用 Python 30分钟 教你快速搭建一个博客 为防有的同学不清楚 Sphinx ,这...

王炳明
昨天
5
0
黑客之道-40本书籍助你快速入门黑客技术免费下载

场景 黑客是一个中文词语,皆源自英文hacker,随着灰鸽子的出现,灰鸽子成为了很多假借黑客名义控制他人电脑的黑客技术,于是出现了“骇客”与"黑客"分家。2012年电影频道节目中心出品的电影...

badaoliumang
昨天
16
0
很遗憾,没有一篇文章能讲清楚线程的生命周期!

(手机横屏看源码更方便) 注:java源码分析部分如无特殊说明均基于 java8 版本。 简介 大家都知道线程是有生命周期,但是彤哥可以认真负责地告诉你网上几乎没有一篇文章讲得是完全正确的。 ...

彤哥读源码
昨天
19
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部