/**
* - void set(Object value)设置当前线程的线程局部变量的值。
* - public Object get()该方法返回当前线程所对应的线程局部变量。
* - public void remove()将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。
* 需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。
* - protected Object initialValue()返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。
* 这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。
*/
public class ThreadLocalDemo extends Thread{
private Res res; public ThreadLocalDemo(Res res) {
this.res = res;
} @Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName()+"-i:"+i+"-num:"+res.getNum());
}
}
public static void main(String[] args){
Res res = new Res();
new ThreadLocalDemo(res).start();
new ThreadLocalDemo(res).start();
new ThreadLocalDemo(res).start();
}
//Thread-0-i:0-num:1
//Thread-0-i:1-num:2
//Thread-0-i:2-num:3
//Thread-1-i:0-num:1
//Thread-1-i:1-num:2
//Thread-1-i:2-num:3
//Thread-2-i:0-num:1
//Thread-2-i:1-num:2
//Thread-2-i:2-num:3
} class Res{
public static Integer count = 0;
public static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
return 0;
}
};
public Integer getNum(){
int count = threadLocal.get() + 1;
threadLocal.set(count);
return count;
}
}
/**
* 如果你创建了 一个 ThreadLocal 变量,那么访问这个变量的每个线程都会有这个变量的一个本地副本。
* 当多个线程操作这个变量时,实际操作的是自己本地内存里面的变量,从而避免了线程安全问题
*/
public class ThreadLocalDemo {
static ThreadLocal<String> local = new ThreadLocal<>();
static void print(String s){
System.out.println(s+":"+local.get());
local.remove();
}
public static void main(String[] args){
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
local.set("threadA");
print("threadA");
System.out.println("threadA remove after:"+ local.get());
}
});
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
local.set("threadB");
print("threadB");
System.out.println("threadB remove after:"+ local.get());
}
});
threadA.start();
threadB.start();
//threadB:threadB
//threadA:threadA
//threadB remove after:threadB
//threadA remove after:threadA //执行local.remove();
//threadA:threadA
//threadB:threadB
//threadA remove after:null
//threadB remove after:null
}
}
public class ThreadLocalDemo2 {
static ThreadLocal<String> local = new ThreadLocal<>();
public static void main(String[] args){
local.set("hello");
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("thread:"+local.get());
}
});
threadA.start();
System.out.println("main:"+local.get());
//thread:null
//main:hello
//同一个 ThreadLocal 变量在父线程中被设置值后, 在子线程中是获取不到的。
//因为在子线程 threadA 里面调用 get 方法时当前线程 为 threadA 线程,而这里调用 set 方法设置线程变量的是 main 线程,
// 两者是不同的线程,自然子线程访问时返回 null
}
}
/**
* InheritableThreadLocal
* 子线程获取父线程的threadlocal变量
*/
public class ThreadLocalDemo3 {
static ThreadLocal<String> local = new InheritableThreadLocal<>();
public static void main(String[] args){
local.set("hello");
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("thread:"+local.get());
}
});
threadA.start();
System.out.println("main:"+local.get());
//main:hello
//thread:hello
//当父线程创建子线程时,构造函数会把父线程中 inheritableThreadLocals 变量里面的本地变量复制一份保存到子线程的 inheritableThreadLocals 变量里面
}
}

简单使用


import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class ParseDate{
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static class Parse implements Runnable{
int i = 0;
public Parse(int i) {
this.i = i;
} @Override
public void run() {
try {
Date t = sdf.parse("2019-01-10 19:12:" + i % 60);
System.out.println(i+":"+t);
}catch (ParseException e){
e.printStackTrace();
}
}
}
public static void main(String[] args){
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 1000; i++) {
executorService.execute(new Parse(i));
}
}
//Exception in thread "pool-1-thread-121" Exception in thread "pool-1-thread-123" Exception in thread "pool-1-thread-120" java.lang.NumberFormatException: multiple points
// at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1890)
// at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
// at java.lang.Double.parseDouble(Double.java:538) //SimpleDateFormat.parse()方法并不是线程安全的,因此在线程池中共享这个对象必然导致错误 }

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; /**
* 为每一个线程分配不同的对象,需要在应用层面保证,ThreadLocal只是起到了简单的容器作用
*/
public class ParseDate2 {
static ThreadLocal<SimpleDateFormat> t1 = new ThreadLocal<>();
public static class Parse implements Runnable{
int i = 0;
public Parse(int i) {
this.i = i;
} @Override
public void run() {
try {
if (t1.get()==null){
t1.set(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
}
Date t = t1.get().parse("2019-01-10 19:12:" + i % 60);
System.out.println(i+":"+t);
}catch (ParseException e){
e.printStackTrace();
}
}
}
public static void main(String[] args){
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 1000; i++) {
executorService.execute(new Parse(i));
}
}
}

10.ThreadLocal的更多相关文章

  1. 浅谈Java引用和Threadlocal的那些事

      这篇文章主要介绍了Java引用和Threadlocal的那些事,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随小编过来看看吧 1 背景 某一天在某一个群里面的某个群友突然提出了一个问 ...

  2. ThreadLocal原理记录,别被坑了!!

    简介 ThreadLocal的用处 ThreadLocal是为了将数据记录一份到某个线程里,确保该数据线程安全 例如数据库的Connection放入ThreadLocal,一个事务会用到很多DAO,但 ...

  3. javaweb学习总结—Apache的DBUtils框架学习

    注明: 本文转载自http://www.cnblogs.com/xdp-gacl/p/4007225.html 一.commons-dbutils简介 commons-dbutils 是 Apache ...

  4. javaweb学习总结(四十一)——Apache的DBUtils框架学习

    一.commons-dbutils简介 commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化 ...

  5. 理论篇-Java中一些零碎的知识点

    1. Java中length,length方法,size方法区别 length属性:用于获取数组长度. length方法:用于获取字符串长度. size方法:用于获取泛型集合有多少个元素. 2. is ...

  6. java之高并发与多线程

    进程和线程的区别和联系 从资源占用,切换效率,通信方式等方面解答 线程具有许多传统进程所具有的特征,故又称为轻型进程(Light—Weight Process)或进程元:而把传统的进程称为重型进程(H ...

  7. Apache的DBUtils框架学习(转)

    一.commons-dbutils简介 commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化 ...

  8. 面渣逆袭:Java并发六十问,快来看看你会多少道!

    大家好,我是老三,面渣逆袭 继续,这节我们来盘一盘另一个面试必问知识点--Java并发. 这篇文章有点长,四万字,图文详解六十道Java并发面试题.人已经肝麻了,大家可以点赞.收藏慢慢看!扶我起来,我 ...

  9. 并发王者课-铂金10:能工巧匠-ThreadLocal如何为线程打造私有数据空间

    欢迎来到<并发王者课>,本文是该系列文章中的第23篇,铂金中的第10篇. 说起ThreadLocal,相信你对它的名字一定不陌生.在并发编程中,它有着较高的出场率,并且也是面试中的高频面试 ...

随机推荐

  1. 26.LockSupport线程阻塞工具

    import java.util.concurrent.locks.LockSupport; /** * 线程阻塞工具类:LockSupport * 可以在线程内任意位置让线程阻塞 */ public ...

  2. 如何使用Keka for Mac提取受密码保护的文件?用Keka提取文件的格式

    如何使用Keka for Mac提取受密码保护的文件?keka Mac是很多人喜欢的压缩解压工具,以小巧,使用简单,界面简洁受到很多Mac用户的喜欢,你还可以使用它提取文件,下面我们就来介绍一下关于用 ...

  3. JS中的作用域及闭包

    1.JS中的作用域 在 es6 出现之前JS中只有全局作用域和函数作用域,没有块级作用域,即 JS 在函数体内有自己的作用域,但是如果不是在函数体的话就全部都是全局作用域.比如在 if.for 等有 ...

  4. 104、Tensorflow 的变量重用

    import tensorflow as tf # 在不同的变量域中调用conv_relu,并且声明我们想创建新的变量 def my_image_filter(input_images): with ...

  5. shell脚本——注释(单行注释 多行注释)

    参考 : https://blog.csdn.net/weixin_42167759/article/details/80703570 单行注释 以"#"开头的行就是注释,会被解释 ...

  6. delphi 在代码中 添加 TO-DO 并且 管理

    TO-DO List是一项非常好用的功能.采用她可以让我们很清楚的了解以前完成了那些任务,还有哪些任务需要做,由谁负责完成,是不是比较紧急的任务等.今天来不及完成的,明天上班就可以很快的找到任务所在的 ...

  7. mybatis分页插件使用

    一:导入依赖 <!--分页插件--> <dependency> <groupId>com.github.pagehelper</groupId> < ...

  8. String的replace导致内存溢出

    从一次内存溢出来看JDK的String应该怎么用 背景 JDK在String类中给我们提供的API,replace是个使用频率很高的的方法.因为他可以对字符串内容进行替换,只需要输入替换字符串和被替换 ...

  9. 组件化框架设计之Java SPI机制(三)

    阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680 本篇文章将从深入理解java SPI机制来介绍组件化框架设计: ...

  10. C++继承中的构造和析构

    1,构造:对象在创建的后所要做的一系列初始化的工作: 析构:对象在摧毁之前所要做的一系列清理工作: 2,思考: 1,子类中如何初始化父类成员? 1,对于继承而言,子类可以获得父类的代码,可以获得父类中 ...