Java并发编程与技术内幕:ThreadFactory、ThreadLocal
林炳文Evankaka原创作品。转载请注明出处http://blog.csdn.net/evankaka
摘要:本文主要讲了ThreadFactory、ThreadLocal的实例和源码解析
一、ThreadFactory
1.1 源码解读
ThreadFactory这个故名思义,就是一个线程工厂。用来创建线程。这里为什么要使用线程工厂呢?其实就是为了统一在创建线程时设置一些参数,如是否守护线程。线程一些特性等,如优先级。通过这个TreadFactory创建出来的线程能保证有相同的特性。下面来看看它的源码吧
它首先是一个接口类,而且方法只有一个。就是创建一个线程。
public interface ThreadFactory {Thread newThread(Runnable r);
}
在JDK中,有实现ThreadFactory就只有一个地方。而更多的时候,我们都是继承它然后自己来写这个线程工厂的。
下面的代码中在类Executors当中。默认的 我们创建线程池时使用的就是这个线程工厂
static class DefaultThreadFactory implements ThreadFactory {private static final AtomicInteger poolNumber = new AtomicInteger(1);//原子类,线程池编号private final ThreadGroup group;//线程组private final AtomicInteger threadNumber = new AtomicInteger(1);//线程数目private final String namePrefix;//为每个创建的线程添加的前缀DefaultThreadFactory() {SecurityManager s = System.getSecurityManager();group = (s != null) ? s.getThreadGroup() :Thread.currentThread().getThreadGroup();//取得线程组namePrefix = "pool-" +poolNumber.getAndIncrement() +"-thread-";}public Thread newThread(Runnable r) {Thread t = new Thread(group, r,namePrefix + threadNumber.getAndIncrement(),0);//真正创建线程的地方,设置了线程的线程组及线程名if (t.isDaemon())t.setDaemon(false);if (t.getPriority() != Thread.NORM_PRIORITY)//默认是正常优先级t.setPriority(Thread.NORM_PRIORITY);return t;}}
在上面的代码中,可以看到线程池中默认的线程工厂实现是很简单的,它做的事就是统一给线程池中的线程设置线程group、统一的线程前缀名。以及统一的优先级。
1.2 应用实例
下面来看看自己写的一个线程工厂
package com.func.axc.threadfactory;import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ThreadFactory;/*** 功能概要:* * @author linbingwen* @since 2016年6月18日*/
public class ThreadFactoryTest {static class MyThreadFactory implements ThreadFactory {private int counter;private String name;private List<String> stats;public MyThreadFactory(String name) {counter = 0;this.name = name;stats = new ArrayList<String>();}@Overridepublic Thread newThread(Runnable run) {Thread t = new Thread(run, name + "-Thread-" + counter);counter++;stats.add(String.format("Created thread %d with name %s on%s\n",t.getId(), t.getName(), new Date()));return t;}public String getStas() {StringBuffer buffer = new StringBuffer();Iterator<String> it = stats.iterator();while (it.hasNext()) {buffer.append(it.next());buffer.append("\n");}return buffer.toString();}}static class MyTask implements Runnable {private int num;public MyTask(int num) {this.num = num;}@Overridepublic void run() {System.out.println("Task "+ num+" is running");try {Thread.sleep(2*10000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}/*** @author linbingwen* @since 2016年6月18日* @param args*/public static void main(String[] args) {System.out.println("main thread beging");MyThreadFactory factory = new MyThreadFactory("MyThreadFactory"); Thread thread = null; for(int i = 0; i < 10; i++) { thread = factory.newThread(new MyTask(i)); thread.start(); } System.out.printf("Factory stats:\n"); System.out.printf("%s\n",factory.getStas()); System.out.println("main thread end");}}
输出结果:
这里通过线程工厂统一设置了线程前缀名,并将创建的线程放到一个list当中。
二、ThreadLocal源码与应用
2.1 应用实例
其实这个和上面的ThreadFactoy基本没什么关联。ThreadFactory与ThreadGroup还有点关联。ThreadLocal基本上和这两个没什么联系的,但是在高并发场景,如果只考虑线程安全而不考虑延迟性、数据共享的话,那么使用ThreadLocal会是一个非常不错的选择。
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
应用实例:
package com.func.axc.threadlocal;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** 功能概要:* * @author linbingwen* @since 2016年6月18日 */
public class ThreadLocalTest {//创建一个Integer型的线程本地变量static final ThreadLocal<Integer> local = new ThreadLocal<Integer>() {@Overrideprotected Integer initialValue() {return 0;}};static class Task implements Runnable{private int num;public Task(int num) {this.num = num;}@Overridepublic void run() {//获取当前线程的本地变量,然后累加10次Integer i = local.get();while(++i<10);System.out.println("Task " + num + "local num resutl is " + i);}}static void Test1(){System.out.println("main thread begin");ExecutorService executors = Executors.newCachedThreadPool();for(int i =1;i<=5;i++) {executors.execute(new Task(i));}executors.shutdown();System.out.println("main thread end");}public static void main(String[] args){Test1();}}
输出结果:
可以看到各个线程之间的变量是独门的,不会相影响。
通过上面的一个实例,简单的了解了ThreadLocal的用法,下面再来看下其源码实现。
2.2 源码解析
首先是其包含的方法:
它的构造函数不做什么:
public ThreadLocal() {}
其实主要的也就下面几个方法:
public T get() { }
public void set(T value) { }
public void remove() { }
protected T initialValue() { }
(1)get
public T get() {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}return setInitialValue();}
这个上方法就是用来取得变量的副本的,注意到它先取得了当前线程对象,接下来使用了getMap返回一个ThreadLocalMap
ThreadLocalMap getMap(Thread t) {return t.threadLocals;}
然后可以知道ThreadLocalMap这个竟然是从线程中取到的,好,再打开线程类看看
发现Thread类中有这样一个变量:
ThreadLocal.ThreadLocalMap threadLocals = null;
也变是说每一个线程都有自己一个ThreadLocalMap。
在我们第一次调用get()函数 时,getMap函数返回的是一个null的map.接着就调用setInitialValue()
看看setInitialValue,它才是真正去初始化map的地方!
private T setInitialValue() {T value = initialValue();Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);return value;}
其中initialValue这个方法就是我们要重写的,一般我们在这里通过一个new 方法返回一个新的变量实例
protected T initialValue() {return null;}
因为是第一次调用get(),所以getMap后的map还是为null。这时就调用到createMap
void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);}
终于创建ThreadLocalMap!
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {table = new Entry[INITIAL_CAPACITY];int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);table[i] = new Entry(firstKey, firstValue);size = 1;setThreshold(INITIAL_CAPACITY);}
这里就将Thread和我们的ThreadLocal通过一个map关联起来。意思是每个Thread中都有一个ThreadLocal.ThreadLocalMap。其中Key为ThreadLocal这个实例,value为每次initialValue()得到的变量!
接下来如果我们第二次调用get()函数,这里就会进入if方法中去!
public T get() {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}return setInitialValue();}
进入If方法中后。就会根据当前的thradLocal实例为Key,取得thread中对应map的vale.其中getEntry方法只是取得我们的key-value对。注意,这时的table其实就是在ThreadLocal实例中都会记录着每个和它关联的Thread类中的ThreadLocalMap变量
private Entry getEntry(ThreadLocal<?> key) {int i = key.threadLocalHashCode & (table.length - 1);Entry e = table[i];if (e != null && e.get() == key)return e;elsereturn getEntryAfterMiss(key, i, e);}
是取得我们的key-value对之后就可取value了,然后就是返回result.如果这时取不到entry,那么又会调用到setInitalValue()方法,过程又和上面的一样了。这里就不说了!
(2)set
这个方法就是重新设置每一个线程的本地ThreadLocal变量的值
public void set(T value) {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);}
这里取得当前线程,然后根据当前的thradLocal实例取得其map。然后重新设置 map.set(this, value);这时这个线程的thradLocal里的变量副本就被重新设置值了!
(3)remove
就是清空ThreadLocalMap里的value,这样一来。下次再调用get时又会调用 到initialValue这个方法返回设置的初始值
public void remove() {ThreadLocalMap m = getMap(Thread.currentThread());if (m != null)m.remove(this);}
总结:
1、每个线程都有自己的局部变量
每个线程都有一个独立于其他线程的上下文来保存这个变量,一个线程的本地变量对其他线程是不可见的(有前提,后面解释)
2、独立于变量的初始化副本
ThreadLocal可以给一个初始值,而每个线程都会获得这个初始化值的一个副本,这样才能保证不同的线程都有一份拷贝。
3、状态与某一个线程相关联
ThreadLocal 不是用于解决共享变量的问题的,不是为了协调线程同步而存在,而是为了方便每个线程处理自己的状态而引入的一个机制,理解这点对正确使用ThreadLocal至关重要。
Java并发编程与技术内幕:ThreadFactory、ThreadLocal相关推荐
- Java并发编程与技术内幕:线程池深入理解
林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 摘要: 本文主要讲了Java当中的线程池的使用方法.注意事项及其实现源码实现原理,并辅以实例加 ...
- Java并发编程与技术内幕:ConcurrentHashMap源码解析
林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 摘要:本文主要讲了Java中ConcurrentHashMap 的源码 ConcurrentH ...
- Java并发编程:线程封闭和ThreadLocal详解
什么是线程封闭 当访问共享变量时,往往需要加锁来保证数据同步.一种避免使用同步的方式就是不共享数据.如果仅在单线程中访问数据,就不需要同步了.这种技术称为线程封闭.在Java语言中,提供了一些类库和机 ...
- Java并发编程基础--ThreadLocal
Java并发编程基础之ThreadLocal ThreadLocal是一个线程变量,但本质上是一个以ThreadLocal对象为键.任意对象为值的存储结构,这个结构依附在线程上,线程可以根据一个T ...
- Java并发编程:ThreadLocal
Java并发编程:深入剖析ThreadLocal ThreadLocal ThreadLocal之我见 Struts2中的设计模式----ThreadLocal模式 ThreadLocal Threa ...
- [转载]Java并发编程:深入剖析ThreadLocal
原文地址:http://www.cnblogs.com/dolphin0520/p/3920407.html 想必很多朋友对ThreadLocal并不陌生,今天我们就来一起探讨 ...
- Java并发编程技术大纲
Java并发编程技术大纲:
- java 并发框架源码_某网Java并发编程高阶技术-高性能并发框架源码解析与实战(云盘下载)...
第1章 课程介绍(Java并发编程进阶课程) 什么是Disruptor?它一个高性能的异步处理框架,号称"单线程每秒可处理600W个订单"的神器,本课程目标:彻底精通一个如此优秀的 ...
- java 并发编程总结
这边文章的主要内容是基于"java并发编程艺术"这本书,中间加入了一些自己的理解.这篇文章包括并发编程涉及到的几乎所有基础知识.主要是帮助长期从事业务逻辑开发的java程序员梳理一 ...
最新文章
- vue组件系列2、拖放上传
- java主类结构设计,设计结构(1)
- Vue视频教程系列第三十七节-子路由地配置
- MySQL 存储引擎 | MyISAM 与 InnoDB
- C++11 并发指南二(std::thread 详解)
- CSS:实现文本超出显示省略效果(可指定几行显示)
- Python生成器 yield
- express 学习记录
- 低秩矩阵完备_矩阵之芯 SVD: 基本应用以及与其他分解的关系
- java适配器模式_java设计模式之适配器模式
- Tc wintc turbo c 写的有关图形的小程序 tc图形程序 带按键处理
- orc识别 语音识别 云真机 内网穿透快速调研
- 湖南大学计算机博士好考吗,湖南大学最年轻的副教授是什么水平
- PAT乙级1068 万绿丛中一点红 (20 分)
- 一寸照片电子版怎么弄?电子证件照制作方法
- 失败一时并不可怕,可怕的是不去总结和改正。
- c语言更改记事本改为大写,pdf英文字母小写改大写怎么改
- Android 毛小软件,安卓薅羊毛软件
- Linux 获取毫秒级时间戳
- 二手书交易系统用例图
热门文章
- 京东面试官:从求职者到面试官的心路历程
- 45个python入门案例_4牛-三博股票学习网
- 计算机操作系统底层相关知识
- 2018尚硅谷SpringBoot视频教程附代码+笔记+课件(内含Docker)
- 数字信号处理实验4:用窗口法设计FIR滤波器
- linux卸载内核网卡驱动,ubuntu 16.04 卸载旧网卡noveau装新的网卡驱动
- 开源邮件客户端_排名前6位的开源桌面电子邮件客户端
- 2.阿里实人认证 .net 准备工作2 转换demo
- 【 长尾理论在现实中的巨大威力】
- 尼尔机器人技能快捷键_尼尔机械纪元出招表及招式使用技巧 尼尔机械纪元技能怎么用...