提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

ThreadLocal

  • Threadlocal使用场景
  • 一、故事从两个线程开始
  • 二、线程池来帮忙,却好心帮坏事
    • 1. 10个线程打印日期
    • 2、1000个线程打印日期
    • 3.使用Synchronized解决线程安全问题
    • 4.使用ThreadLocal解决线程安全问题
  • 三、 ThreadLocal第二种用法
  • 四.总结
    • 两个作用
    • 两个场景
    • 使用ThreadLocal的好处

Threadlocal使用场景

一、故事从两个线程开始

ThreadLocal与synchronized同步机制的比较
在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。
ThreadLocal是线程局部变量,是一种多线程间并发访问变量的解决方案。和synchronized等加锁的方式不同,ThreadLocal完全不提供锁,而使用以空间换时间的方式,为每个线程提供变量的独立副本,以保证线程的安全。

代码如下(示例):

package threadlocal;import java.text.SimpleDateFormat;
import java.util.Date;/*** 描述:     两个线程打印日期*/
public class ThreadLocalNormalUsage00 {public static void main(String[] args) {new Thread(new Runnable() {@Overridepublic void run() {String date = new ThreadLocalNormalUsage00().date(10);System.out.println(date);}}).start();new Thread(new Runnable() {@Overridepublic void run() {String date = new ThreadLocalNormalUsage00().date(1047);System.out.println(date);}}).start();}public String date(int seconds) {//参数的单位是毫秒,从1970.1.1 00:00:00 GMT计时Date date = new Date(1000 * seconds);SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");return dateFormat.format(date);}
}

二、线程池来帮忙,却好心帮坏事

1. 10个线程打印日期

代码如下(示例):

10个线程打印日期

package threadlocal;import java.text.SimpleDateFormat;
import java.util.Date;/*** 描述:     10个线程打印日期*/
public class ThreadLocalNormalUsage01 {public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 30; i++) {int finalI = i;new Thread(new Runnable() {@Overridepublic void run() {String date = new ThreadLocalNormalUsage01().date(finalI);System.out.println(date);}}).start();Thread.sleep(100);}}public String date(int seconds) {//参数的单位是毫秒,从1970.1.1 00:00:00 GMT计时Date date = new Date(1000 * seconds);SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");return dateFormat.format(date);}
}

结果:

2、1000个线程打印日期

package threadlocal;import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** 描述:     1000个打印日期的任务,用线程池来执行*/
public class ThreadLocalNormalUsage02 {public static ExecutorService threadPool = Executors.newFixedThreadPool(10);public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 1000; i++) {int finalI = i;threadPool.submit(new Runnable() {@Overridepublic void run() {String date = new ThreadLocalNormalUsage02().date(finalI);System.out.println(date);}});}threadPool.shutdown();}public String date(int seconds) {//参数的单位是毫秒,从1970.1.1 00:00:00 GMT计时Date date = new Date(1000 * seconds);SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");return dateFormat.format(date);}
}

结果:

  • 将ThreadLocalNormalUsage02改进后,1000个线程共享DateFormat对象
package threadlocal;import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** 描述:     1000个打印日期的任务,用线程池来执行*/
public class ThreadLocalNormalUsage03 {public static ExecutorService threadPool = Executors.newFixedThreadPool(10);static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 1000; i++) {int finalI = i;threadPool.submit(new Runnable() {@Overridepublic void run() {String date = new ThreadLocalNormalUsage03().date(finalI);System.out.println(date);}});}threadPool.shutdown();}public String date(int seconds) {//参数的单位是毫秒,从1970.1.1 00:00:00 GMT计时Date date = new Date(1000 * seconds);return dateFormat.format(date);}
}

结果:

发现发生了线程安全问题,所有线程共用一个SimpleDateFormat,有些时间是一样的

3.使用Synchronized解决线程安全问题

使用Synchronized解决线程安全问题

package threadlocal;import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** 描述:     加锁来解决线程安全问题*/
public class ThreadLocalNormalUsage04 {public static ExecutorService threadPool = Executors.newFixedThreadPool(10);static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 1000; i++) {int finalI = i;threadPool.submit(new Runnable() {@Overridepublic void run() {String date = new ThreadLocalNormalUsage04().date(finalI);System.out.println(date);}});}threadPool.shutdown();}public String date(int seconds) {//参数的单位是毫秒,从1970.1.1 00:00:00 GMT计时Date date = new Date(1000 * seconds);String s = null;synchronized (ThreadLocalNormalUsage04.class) {s = dateFormat.format(date);}return s;}
}

结果:

虽然解决了并发线程安全问题,但是效率太低了。


4.使用ThreadLocal解决线程安全问题

提示:这里对文章进行总结:
由于有10个线程执行任务(打印日期),创建10个dateformat对象,这样既没有性能损耗,保证线程安全(每个dateformat独享独有),也避免了synchornized带来的一直等待排队的问题。

利用ThreadLocal,给每个线程分配自己的dateFormat对象,保证了线程安全,高效利用内存

package threadlocal;import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** 描述:     利用ThreadLocal,给每个线程分配自己的dateFormat对象,保证了线程安全,高效利用内存*/
public class ThreadLocalNormalUsage05 {public static ExecutorService threadPool = Executors.newFixedThreadPool(10);public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 1000; i++) {int finalI = i;threadPool.submit(new Runnable() {@Overridepublic void run() {String date = new ThreadLocalNormalUsage05().date(finalI);System.out.println(date);}});}threadPool.shutdown();}public String date(int seconds) {//参数的单位是毫秒,从1970.1.1 00:00:00 GMT计时Date date = new Date(1000 * seconds);
//        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");SimpleDateFormat dateFormat = ThreadSafeFormatter.dateFormatThreadLocal2.get();return dateFormat.format(date);}
}class ThreadSafeFormatter {public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = new ThreadLocal<SimpleDateFormat>() {@Overrideprotected SimpleDateFormat initialValue() {return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");}};public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal2 = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
}

运行结果:

三、 ThreadLocal第二种用法

这里每个线程内保存全局变量,可以让不同方法直接使用,避免参数传递麻烦

## 更好的方法是用Threadlocal,这样无需synchronized,可以在不影响性能的情况下,无需层层传递参数,就可达到保存当前线程对应的用户信息的目的。

package threadlocal;/*** 描述:     演示ThreadLocal用法2:避免传递参数的麻烦*/
public class ThreadLocalNormalUsage06 {public static void main(String[] args) {new Service1().process("");}
}class Service1 {public void process(String name) {User user = new User("超哥");UserContextHolder.holder.set(user);new Service2().process();}
}class Service2 {public void process() {User user = UserContextHolder.holder.get();ThreadSafeFormatter.dateFormatThreadLocal.get();System.out.println("Service2拿到用户名:" + user.name);new Service3().process();}
}class Service3 {public void process() {User user = UserContextHolder.holder.get();System.out.println("Service3拿到用户名:" + user.name);UserContextHolder.holder.remove();}
}class UserContextHolder {public static ThreadLocal<User> holder = new ThreadLocal<>();}class User {String name;public User(String name) {this.name = name;}
}

运行结果:

四.总结

两个作用

两个场景


使用ThreadLocal的好处

ThreadLocal一次性解决老大难问题相关推荐

  1. Tomcat启动错误-Unable to open debugger port (127.0.0.4322)一次性解决方式

    Tomcat启动错误-Unable to open debugger port (127.0.0.4322)一次性解决方式 原因分析 端口已经被占用. 解决方式 大部分帖子都使用了两种方法:1.在配置 ...

  2. Word 安装Mathtype后无法使用CTRL+C和CTRL+V【一次性解决】

    Word 安装Mathtype后无法使用CTRL+C和CTRL+V[一次性解决] 问题    Word 2019 安装Mathtype后,无法使用CTRL+C和CTRL+V,网上很多方法是,文件-&g ...

  3. 一次性解决office部署问题(即点即用等)

    前言 因为之前电脑安装了office2019,后面需要安装Visio,下载安装时报错30204-44,查看发现之前安装的office版本是即点即用版,可能这两者不兼容.网上搜索教程等,最后发现一个工具 ...

  4. 一次性解决idea新建项目src文件夹无法创建Java class文件的问题!

    一次性解决idea新建项目src文件夹无法创建Java class文件的问题! 再pom文件中添加jar 后发现java已经不是黑色文件了 出现这种情况是因为我们用idea创建sping项目时会自动把 ...

  5. 自媒体必备2大运营工具+5款剪辑软件,一次性解决,让你事半功倍

    还在苦苦寻找多功能软件? 想一次性解决多重问题? 2大运营工具+5款剪辑软件让你成为自媒体大神,高效.省时.省力,让你事半功倍. 一.新媒体管家Plus (1)特点: 1.简洁.易用的新媒体管理工具; ...

  6. 扫地机器人的轮子困住_扫地机器人老被困住,我来告诉你怎么一次性解决

    原标题:扫地机器人老被困住,我来告诉你怎么一次性解决 扫地机器人可以给我们带来干净整洁的家,帮我们节省更多的业余时间.但是在使用扫地机的时候也会遇见很多的问题,最烦人的就是老被困住,动不动就要求助,这 ...

  7. 戴森吸尘器搭配洒拖T5擦地吸尘头,一次性解决吸尘、拖地

    "戴森拖把头"是戴森用户都会关注的一个话题,我也不例外.据商家描述,它能让戴森学会拖地,确实是个不错Idea!相比先吸尘再拖地的清洁过程,相信这种一次性解决吸尘.拖地问题的方法,更 ...

  8. 一次性解决:IDEA的 maven 配置问题,在新项目中不再担心 maven 的配置问题

    Ⅰ.问题描述: 1.是否安装新的 maven 版本: 现在新出的IDEA很可能是maven已经集成了(即:自己自带的有maven环境),所以在确定是否安装其它版本的 maven 之前,最好确定一下,是 ...

  9. java完全背包,一次性解决三种背包问题

    前言 首先,大概讲一下什么是"背包"问题:背包问题是指你有一个容量为V的背包,然后有n个物品在你面前,你要怎么装才能使得背包里的物品总价值最大.而每种物品是只有1个,还是有多个,亦 ...

最新文章

  1. 商品和服务税收分类编码导出_谨慎选择加拿大商标的商品和服务分类!否则支付额外费用...
  2. Spring(十八):Spring AOP(二):通知(前置、后置、返回、异常、环绕)
  3. PAT甲级1068 Find More Coins (30 分):[C++题解]DP、背包问题、dp输出方案
  4. 2021年炼丹笔记最受欢迎的10篇技术文章
  5. 6.5 scp:远程文件复制
  6. java drawimage 参数_小程序中canvas的drawImage方法参数使用详解
  7. oracle consistent gets,oracle构建一致性读
  8. vm8中装了redhat9 在安装vmwaretools的时候老是出现gcc位置错误
  9. MangoTrainingCourse课程hands-on lab-1
  10. java计算立方体体积(利用类函数)
  11. 永磁同步电机转速电流双闭环PI+MTPA+弱磁控制Simulink仿真模型
  12. 2021腾讯算法大赛
  13. elementui组件中,树形组件的使用
  14. Virtualbox安装Kylin 10后调整屏幕分辨率无法选中保存按钮
  15. Flowable工作流引擎
  16. pygame安装(参考:烟雨平生cj)
  17. 电脑计算机不显示桌面了怎么办,电脑不显示桌面黑屏了 电脑黑屏不显示桌面怎么办 - 云骑士一键重装系统...
  18. ORCALE 19C数据库Linux系统数据泵导入步骤
  19. Godot全局插件支持库
  20. 服务的定义,使用和绑定(FileManagerService)

热门文章

  1. 进阶高级自动化测试测试,Docker 常遇问题整理(带解决方案)
  2. 【Docker】docker日常工作总结(涉及基础命令、基础名词概念、镜像、容器、网络、数据卷)
  3. 【论文】AlexNet 一
  4. PID控制里面积分控制为什么能消除静态误差及微分控制为什么能减少超调量的原因
  5. SQLSERVER数据库质疑解决方案
  6. h5应用数据加密_H5+应用打包JS没有加密混淆
  7. Word文档如何进行压缩文件?
  8. 粒子的散射模拟matlab程序,基于Matlab的α粒子的散射实验模拟.pdf
  9. 手机中PDF格式转换PPT操作方法
  10. macbook黑屏_MacBook维修 苹果笔记本电脑进水不开机