林炳文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相关推荐

  1. Java并发编程与技术内幕:线程池深入理解

    林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 摘要: 本文主要讲了Java当中的线程池的使用方法.注意事项及其实现源码实现原理,并辅以实例加 ...

  2. Java并发编程与技术内幕:ConcurrentHashMap源码解析

    林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 摘要:本文主要讲了Java中ConcurrentHashMap 的源码 ConcurrentH ...

  3. Java并发编程:线程封闭和ThreadLocal详解

    什么是线程封闭 当访问共享变量时,往往需要加锁来保证数据同步.一种避免使用同步的方式就是不共享数据.如果仅在单线程中访问数据,就不需要同步了.这种技术称为线程封闭.在Java语言中,提供了一些类库和机 ...

  4. Java并发编程基础--ThreadLocal

    Java并发编程基础之ThreadLocal ​ ThreadLocal是一个线程变量,但本质上是一个以ThreadLocal对象为键.任意对象为值的存储结构,这个结构依附在线程上,线程可以根据一个T ...

  5. Java并发编程:ThreadLocal

    Java并发编程:深入剖析ThreadLocal ThreadLocal ThreadLocal之我见 Struts2中的设计模式----ThreadLocal模式 ThreadLocal Threa ...

  6. [转载]Java并发编程:深入剖析ThreadLocal

                原文地址:http://www.cnblogs.com/dolphin0520/p/3920407.html 想必很多朋友对ThreadLocal并不陌生,今天我们就来一起探讨 ...

  7. Java并发编程技术大纲

    Java并发编程技术大纲:

  8. java 并发框架源码_某网Java并发编程高阶技术-高性能并发框架源码解析与实战(云盘下载)...

    第1章 课程介绍(Java并发编程进阶课程) 什么是Disruptor?它一个高性能的异步处理框架,号称"单线程每秒可处理600W个订单"的神器,本课程目标:彻底精通一个如此优秀的 ...

  9. java 并发编程总结

    这边文章的主要内容是基于"java并发编程艺术"这本书,中间加入了一些自己的理解.这篇文章包括并发编程涉及到的几乎所有基础知识.主要是帮助长期从事业务逻辑开发的java程序员梳理一 ...

最新文章

  1. vue组件系列2、拖放上传
  2. java主类结构设计,设计结构(1)
  3. Vue视频教程系列第三十七节-子路由地配置
  4. MySQL 存储引擎 | MyISAM 与 InnoDB
  5. C++11 并发指南二(std::thread 详解)
  6. CSS:实现文本超出显示省略效果(可指定几行显示)
  7. Python生成器 yield
  8. express 学习记录
  9. 低秩矩阵完备_矩阵之芯 SVD: 基本应用以及与其他分解的关系
  10. java适配器模式_java设计模式之适配器模式
  11. Tc wintc turbo c 写的有关图形的小程序 tc图形程序 带按键处理
  12. orc识别 语音识别 云真机 内网穿透快速调研
  13. 湖南大学计算机博士好考吗,湖南大学最年轻的副教授是什么水平
  14. PAT乙级1068 万绿丛中一点红 (20 分)
  15. 一寸照片电子版怎么弄?电子证件照制作方法
  16. 失败一时并不可怕,可怕的是不去总结和改正。
  17. c语言更改记事本改为大写,pdf英文字母小写改大写怎么改
  18. Android 毛小软件,安卓薅羊毛软件
  19. Linux 获取毫秒级时间戳
  20. 二手书交易系统用例图

热门文章

  1. 京东面试官:从求职者到面试官的心路历程
  2. 45个python入门案例_4牛-三博股票学习网
  3. 计算机操作系统底层相关知识
  4. 2018尚硅谷SpringBoot视频教程附代码+笔记+课件(内含Docker)
  5. 数字信号处理实验4:用窗口法设计FIR滤波器
  6. linux卸载内核网卡驱动,ubuntu 16.04 卸载旧网卡noveau装新的网卡驱动
  7. 开源邮件客户端_排名前6位的开源桌面电子邮件客户端
  8. 2.阿里实人认证 .net 准备工作2 转换demo
  9. 【 长尾理论在现实中的巨大威力】
  10. 尼尔机器人技能快捷键_尼尔机械纪元出招表及招式使用技巧 尼尔机械纪元技能怎么用...