Java并发编程与技术内幕:ThreadFactory、ThreadLocal

2024-02-28 10:32

本文主要是介绍Java并发编程与技术内幕:ThreadFactory、ThreadLocal,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、ThreadFactory

1.1 源码解读

ThreadFactory这个故名思义,就是一个线程工厂。用来创建线程。这里为什么要使用线程工厂呢?其实就是为了统一在创建线程时设置一些参数,如是否守护线程。线程一些特性等,如优先级。通过这个TreadFactory创建出来的线程能保证有相同的特性。下面来看看它的源码吧

它首先是一个接口类,而且方法只有一个。就是创建一个线程。

 

 
  1. public interface ThreadFactory {

  2.  
  3. Thread newThread(Runnable r);

  4. }

在JDK中,有实现ThreadFactory就只有一个地方。而更多的时候,我们都是继承它然后自己来写这个线程工厂的。

 

下面的代码中在类Executors当中。默认的 我们创建线程池时使用的就是这个线程工厂

 

 
  1. static class DefaultThreadFactory implements ThreadFactory {

  2. private static final AtomicInteger poolNumber = new AtomicInteger(1);//原子类,线程池编号

  3. private final ThreadGroup group;//线程组

  4. private final AtomicInteger threadNumber = new AtomicInteger(1);//线程数目

  5. private final String namePrefix;//为每个创建的线程添加的前缀

  6.  
  7. DefaultThreadFactory() {

  8. SecurityManager s = System.getSecurityManager();

  9. group = (s != null) ? s.getThreadGroup() :

  10. Thread.currentThread().getThreadGroup();//取得线程组

  11. namePrefix = "pool-" +

  12. poolNumber.getAndIncrement() +

  13. "-thread-";

  14. }

  15.  
  16. public Thread newThread(Runnable r) {

  17. Thread t = new Thread(group, r,

  18. namePrefix + threadNumber.getAndIncrement(),

  19. 0);//真正创建线程的地方,设置了线程的线程组及线程名

  20. if (t.isDaemon())

  21. t.setDaemon(false);

  22. if (t.getPriority() != Thread.NORM_PRIORITY)//默认是正常优先级

  23. t.setPriority(Thread.NORM_PRIORITY);

  24. return t;

  25. }

  26. }


在上面的代码中,可以看到线程池中默认的线程工厂实现是很简单的,它做的事就是统一给线程池中的线程设置线程group、统一的线程前缀名。以及统一的优先级。

 

1.2 应用实例

下面来看看自己写的一个线程工厂

 

 
  1. package com.func.axc.threadfactory;

  2.  
  3. import java.util.ArrayList;

  4. import java.util.Date;

  5. import java.util.Iterator;

  6. import java.util.List;

  7. import java.util.concurrent.ThreadFactory;

  8.  
  9. /**

  10. * 功能概要:

  11. *

  12. * @author linbingwen

  13. * @since 2016年6月18日

  14. */

  15. public class ThreadFactoryTest {

  16.  
  17. static class MyThreadFactory implements ThreadFactory {

  18.  
  19. private int counter;

  20. private String name;

  21. private List<String> stats;

  22.  
  23. public MyThreadFactory(String name) {

  24. counter = 0;

  25. this.name = name;

  26. stats = new ArrayList<String>();

  27. }

  28.  
  29. @Override

  30. public Thread newThread(Runnable run) {

  31. Thread t = new Thread(run, name + "-Thread-" + counter);

  32. counter++;

  33. stats.add(String.format("Created thread %d with name %s on%s\n",t.getId(), t.getName(), new Date()));

  34. return t;

  35. }

  36.  
  37. public String getStas() {

  38. StringBuffer buffer = new StringBuffer();

  39. Iterator<String> it = stats.iterator();

  40. while (it.hasNext()) {

  41. buffer.append(it.next());

  42. buffer.append("\n");

  43. }

  44. return buffer.toString();

  45. }

  46.  
  47. }

  48.  
  49. static class MyTask implements Runnable {

  50.  
  51. private int num;

  52.  
  53. public MyTask(int num) {

  54. this.num = num;

  55. }

  56.  
  57. @Override

  58. public void run() {

  59. System.out.println("Task "+ num+" is running");

  60. try {

  61. Thread.sleep(2*10000);

  62. } catch (InterruptedException e) {

  63. // TODO Auto-generated catch block

  64. e.printStackTrace();

  65. }

  66.  
  67. }

  68.  
  69. }

  70.  
  71. /**

  72. * @author linbingwen

  73. * @since 2016年6月18日

  74. * @param args

  75. */

  76. public static void main(String[] args) {

  77. System.out.println("main thread beging");

  78. MyThreadFactory factory = new MyThreadFactory("MyThreadFactory");

  79.  
  80. Thread thread = null;

  81. for(int i = 0; i < 10; i++) {

  82. thread = factory.newThread(new MyTask(i));

  83. thread.start();

  84. }

  85. System.out.printf("Factory stats:\n");

  86. System.out.printf("%s\n",factory.getStas());

  87. System.out.println("main thread end");

  88. }

  89.  
  90. }

输出结果:

 

 


这里通过线程工厂统一设置了线程前缀名,并将创建的线程放到一个list当中。

 


二、ThreadLocal源码与应用

 2.1 应用实例      

     其实这个和上面的ThreadFactoy基本没什么关联。ThreadFactory与ThreadGroup还有点关联。ThreadLocal基本上和这两个没什么联系的,但是在高并发场景,如果只考虑线程安全而不考虑延迟性、数据共享的话,那么使用ThreadLocal会是一个非常不错的选择。

     当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

应用实例:

 

 
  1. package com.func.axc.threadlocal;

  2.  
  3. import java.util.concurrent.ExecutorService;

  4. import java.util.concurrent.Executors;

  5.  
  6. /**

  7. * 功能概要:

  8. *

  9. * @author linbingwen

  10. * @since 2016年6月18日

  11. */

  12. public class ThreadLocalTest {

  13.  
  14. //创建一个Integer型的线程本地变量

  15. static final ThreadLocal<Integer> local = new ThreadLocal<Integer>() {

  16. @Override

  17. protected Integer initialValue() {

  18. return 0;

  19. }

  20. };

  21.  
  22. static class Task implements Runnable{

  23. private int num;

  24.  
  25. public Task(int num) {

  26. this.num = num;

  27. }

  28.  
  29. @Override

  30. public void run() {

  31. //获取当前线程的本地变量,然后累加10次

  32. Integer i = local.get();

  33. while(++i<10);

  34. System.out.println("Task " + num + "local num resutl is " + i);

  35. }

  36. }

  37.  
  38. static void Test1(){

  39. System.out.println("main thread begin");

  40. ExecutorService executors = Executors.newCachedThreadPool();

  41. for(int i =1;i<=5;i++) {

  42. executors.execute(new Task(i));

  43. }

  44. executors.shutdown();

  45. System.out.println("main thread end");

  46. }

  47.  
  48. public static void main(String[] args){

  49. Test1();

  50. }

  51.  
  52. }

输出结果:

 

可以看到各个线程之间的变量是独门的,不会相影响。

 

 

 

 

通过上面的一个实例,简单的了解了ThreadLocal的用法,下面再来看下其源码实现。

2.2 源码解析

首先是其包含的方法:

它的构造函数不做什么:

 

 
  1. public ThreadLocal() {

  2. }

 

其实主要的也就下面几个方法:

 

 
  1. public T get() { }

  2. public void set(T value) { }

  3. public void remove() { }

  4. protected T initialValue() { }

(1)get

 

 

 
  1. public T get() {

  2. Thread t = Thread.currentThread();

  3. ThreadLocalMap map = getMap(t);

  4. if (map != null) {

  5. ThreadLocalMap.Entry e = map.getEntry(this);

  6. if (e != null) {

  7. @SuppressWarnings("unchecked")

  8. T result = (T)e.value;

  9. return result;

  10. }

  11. }

  12. return setInitialValue();

  13. }


这个上方法就是用来取得变量的副本的,注意到它先取得了当前线程对象,接下来使用了getMap返回一个ThreadLocalMap

 

 

 
  1. ThreadLocalMap getMap(Thread t) {

  2. return t.threadLocals;

  3. }


然后可以知道ThreadLocalMap这个竟然是从线程中取到的,好,再打开线程类看看

 

发现Thread类中有这样一个变量:

 

    ThreadLocal.ThreadLocalMap threadLocals = null;

也变是说每一个线程都有自己一个ThreadLocalMap。

 

在我们第一次调用get()函数 时,getMap函数返回的是一个null的map.接着就调用setInitialValue()

看看setInitialValue,它才是真正去初始化map的地方!

 

 
  1. private T setInitialValue() {

  2. T value = initialValue();

  3. Thread t = Thread.currentThread();

  4. ThreadLocalMap map = getMap(t);

  5. if (map != null)

  6. map.set(this, value);

  7. else

  8. createMap(t, value);

  9. return value;

  10. }


其中initialValue这个方法就是我们要重写的,一般我们在这里通过一个new 方法返回一个新的变量实例

 

 

 
  1. protected T initialValue() {

  2. return null;

  3. }

 

因为是第一次调用get(),所以getMap后的map还是为null。这时就调用到createMap

 

 
  1. void createMap(Thread t, T firstValue) {

  2. t.threadLocals = new ThreadLocalMap(this, firstValue);

  3. }

终于创建ThreadLocalMap!

 
  1. ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {

  2. table = new Entry[INITIAL_CAPACITY];

  3. int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);

  4. table[i] = new Entry(firstKey, firstValue);

  5. size = 1;

  6. setThreshold(INITIAL_CAPACITY);

  7. }

 

这里就将Thread和我们的ThreadLocal通过一个map关联起来。意思是每个Thread中都有一个ThreadLocal.ThreadLocalMap。其中Key为ThreadLocal这个实例,value为每次initialValue()得到的变量!

接下来如果我们第二次调用get()函数,这里就会进入if方法中去!

 

 
  1. public T get() {

  2. Thread t = Thread.currentThread();

  3. ThreadLocalMap map = getMap(t);

  4. if (map != null) {

  5. ThreadLocalMap.Entry e = map.getEntry(this);

  6. if (e != null) {

  7. @SuppressWarnings("unchecked")

  8. T result = (T)e.value;

  9. return result;

  10. }

  11. }

  12. return setInitialValue();

  13. }

进入If方法中后。就会根据当前的thradLocal实例为Key,取得thread中对应map的vale.其中getEntry方法只是取得我们的key-value对。注意,这时的table其实就是在ThreadLocal实例中都会记录着每个和它关联的Thread类中的ThreadLocalMap变量

 

 

 
  1. private Entry getEntry(ThreadLocal<?> key) {

  2. int i = key.threadLocalHashCode & (table.length - 1);

  3. Entry e = table[i];

  4. if (e != null && e.get() == key)

  5. return e;

  6. else

  7. return getEntryAfterMiss(key, i, e);

  8. }

是取得我们的key-value对之后就可取value了,然后就是返回result.如果这时取不到entry,那么又会调用到setInitalValue()方法,过程又和上面的一样了。这里就不说了!

 

(2)set

这个方法就是重新设置每一个线程的本地ThreadLocal变量的值

 

 
  1. public void set(T value) {

  2. Thread t = Thread.currentThread();

  3. ThreadLocalMap map = getMap(t);

  4. if (map != null)

  5. map.set(this, value);

  6. else

  7. createMap(t, value);

  8. }

这里取得当前线程,然后根据当前的thradLocal实例取得其map。然后重新设置 map.set(this, value);这时这个线程的thradLocal里的变量副本就被重新设置值了!

 

(3)remove

就是清空ThreadLocalMap里的value,这样一来。下次再调用get时又会调用 到initialValue这个方法返回设置的初始值

 

 
  1. public void remove() {

  2. ThreadLocalMap m = getMap(Thread.currentThread());

  3. if (m != null)

  4. m.remove(this);

  5. }

 

 

 

总结:

1、每个线程都有自己的局部变量
每个线程都有一个独立于其他线程的上下文来保存这个变量,一个线程的本地变量对其他线程是不可见的(有前提,后面解释)
2、独立于变量的初始化副本
ThreadLocal可以给一个初始值,而每个线程都会获得这个初始化值的一个副本,这样才能保证不同的线程都有一份拷贝。
3、状态与某一个线程相关联
ThreadLocal 不是用于解决共享变量的问题的,不是为了协调线程同步而存在,而是为了方便每个线程处理自己的状态而引入的一个机制,理解这点对正确使用ThreadLocal至关重要。

这篇关于Java并发编程与技术内幕:ThreadFactory、ThreadLocal的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/755241

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

【专题】2024飞行汽车技术全景报告合集PDF分享(附原数据表)

原文链接: https://tecdat.cn/?p=37628 6月16日,小鹏汇天旅航者X2在北京大兴国际机场临空经济区完成首飞,这也是小鹏汇天的产品在京津冀地区进行的首次飞行。小鹏汇天方面还表示,公司准备量产,并计划今年四季度开启预售小鹏汇天分体式飞行汽车,探索分体式飞行汽车城际通勤。阅读原文,获取专题报告合集全文,解锁文末271份飞行汽车相关行业研究报告。 据悉,业内人士对飞行汽车行业