- 浏览: 1326828 次
- 性别:
- 来自: 湖南澧縣
文章分类
最新评论
-
虾米小尹:
不行啊!2.2-0.25=1.9500000000000002 ...
JavaScript浮点数运算 —— 精度问题 -
heluping000000:
引用String a= "abc",首先在 ...
String,到底创建了多少个对象? -
mack:
谢谢分享matcher.appendReplacement(s ...
string.replaceAll()中的特殊字符($ \)与matcher.appendReplacement -
wzt3309:
完全理解,比网上其他资料都要详细
String,到底创建了多少个对象? -
u014771876:
Java中十六进制转换 Integer.toHexString()
首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程(当前线程)自己使用的对象,其他线程是不能访问得到的,各个线程中访问的是不同的对象。另外,说ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每创建一个新的线程就会有一个ThreadLocal.ThreadLocalMap threadLocals,每个线程在适当的时候(在当前线程中第一次访问ThreadLocal的get或set方式时)创建一个这样的threadLocals变量,不是什么对象的拷贝或副本。通过ThreadLocal.set()将这个新创建的对象的引用保存到各线程的自己的一个ThreadLocalMap变量中,每个线程都有这样一个ThreadLocal.ThreadLocalMap threadLocals,执行ThreadLocal.get()时,先会取出当前线程所对应的ThreadLocalMap,再以ThreadLocal实例为Key从当前线程自己的ThreadLocalMap中取出放进去的对象,因此取出来的是各自自己线程中的对象。如果ThreadLocal.set()进去的东西本来就是多个线程共享的同一个对象,那么多个线程的ThreadLocal.get()取得的还是这个共享对象本身,还是有并发访问问题。下面来看看源码:
Thread类中有一个ThreadLocalMap变量,开始是null:
ThreadLocal.ThreadLocalMap threadLocals = null;
由第一次访问ThreadLocal的get或set方法时设置,下面是ThreadLocal中的get与set方法(getMap方法直接使用代码替换过了):
public Object get() { Thread t = Thread.currentThread(); ThreadLocalMap map = t.threadLocals;//取当前线程中的ThreadLocalMap if (map != null) return map.get(this);//从当前线程的ThreadLocalMap变量中取出key为当前ThreadLocal实例的线程局部变量 // Maps are constructed lazily. if the map for this thread // doesn't exist, create it, with this ThreadLocal and its // initial value as its only entry. Object value = initialValue(); createMap(t, value); return value; } public void set(Object value) { Thread t = Thread.currentThread(); ThreadLocalMap map = t.threadLocals; if (map != null) map.set(this, value);//以当前ThreadLocal实例为key,存储线程局部变量到ThreadLocalMap中 else createMap(t, value); }
上面的get与set方法在map为null(第一次访问时,即当前线程中所有的ThreadLocal变量中的第一个第一次访问,只要访问过了,其他的ThreadLocal变量再次访问不会再调用createMap)会调用createMap来为线程Thread类的threadLocals变量赋值,createMap源码如下:
void createMap(Thread t, Object firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
从上面的代码可以看出,每个线程会有一个ThreadLocalMap变量,而ThreadLocalMap中的key为当前ThreadLocal实例。
总之,ThreadLocal 不是用来解决对象共享访问问题的,而主要是提供了保持对象的方法和避免参数传递的方便的对象访问方式。归纳了两点:
1、每个线程中都有一个自己的ThreadLocalMap 类对象,可以将当前线程中某些对象保持到其中,各管各的,线程在适当时候可以正确的访问到这些对象。
2、将一个共用的ThreadLocal 静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中(但key都是同一个,即ThreadLocal 静态实例),然后在线程执行的各处通过这个静态ThreadLocal实例的get()方法取得自己线程保存的那个对象,避免了将这个对象作为参数传递的麻烦。
比如这里要写一个实例,每个线程的日志要记录到各自的日志文件中去,这样避免了多个线程的日志记录到同一文件中时可能出现日志打印顺序混乱,这就要求以线程为范围及,每个线程一个日志文件对象,并且在整个线程中的方法调用栈中使用同一个:
import java.io.PrintWriter; import java.io.FileWriter; import java.io.IOException; public class Logger { private PrintWriter writer = null; // 初始化writer public Logger(String filename) { try { writer = new PrintWriter(new FileWriter(filename)); writer.println("==== Start of log ===="); } catch (IOException e) { e.printStackTrace(); } } // 日志写入文件 public void println(String s) { writer.println(s); } // 关闭文件 public void close() { writer.println("==== End of log ===="); writer.close(); } }
/** * 日志记录器,供所有的线程调用,但每个线程却有独立的Logger * */ public class Log { // 线程局部变量,可以为每个线程创建一个仅且一个的日志文件(其实就是使logger对象线程局部化) private static final ThreadLocal loggerThreadLocal = new ThreadLocal(); // 记录日志 public static void println(String s) { getLogger().println(s); } // 关闭日志 public static void close() { getLogger().close(); } /** * 取得调用Log类的当前线程所持有的Logger日志记录器 * 这样确保每个线程所记录的日志记录到同一文件中 * @return */ private static Logger getLogger() { Logger logger = (Logger) loggerThreadLocal.get(); // 如果线程是第一次呼叫,就建立新挡案并登陆log if (logger == null) { logger = new Logger(Thread.currentThread().getName() + "-log.txt"); // 为每个线程提供唯一一个logger对象 loggerThreadLocal.set(logger); } return logger; } }
public class Service1 { public void method1() { Log.println(Thread.currentThread().getName() + " - Service1.method1()"); new Service2().method2(); } } public class Service2 { public void method2() { Log.println(Thread.currentThread().getName() + " - Service2.method2()"); } }
public class ClientThread extends Thread { public static void main(String[] args) { new ClientThread("A").start(); new ClientThread("B").start(); new ClientThread("C").start(); } public ClientThread(String name) { super(name); } public void run() { System.out.println(getName() + " BEGIN"); for (int i = 0; i < 10; i++) { /* * 每个线程在第一次记录日志时,都会先创建一个新的 Logger ,以后记录 * 会使用第一次创建的 Logger */ Log.println("i = " + i); try { Thread.sleep(100); } catch (InterruptedException e) { } } //关闭时也只是关闭本线程的文件,对其他线程不影响 Log.close(); System.out.println(getName() + " END"); } }
在这个实例中我们启动了3个线程,每个线程都会调用Serivce1的method1方法,再由Service1的method1方法调用Service2的method2方法,这些调用的过程都会记录日志,且日志记录到同一线程日志文件里,且互不干扰。打开的文件流最后在线程运行结束前关闭,这样确保了输出文件流在整个线程中是可用的。所以我们这里的Logger是一个线程局部化的类,每个线程都会需要且只需一这样一个类的实例。在该实例里,我们在每个线程启动时就创建了一个Logger实例,且存储到ThreadLocalMap中,所以我们在Service1与Service2中都能得到这个实例,而不需要把这个实例作为参数从高层模块传递到低层模块中去。
发表评论
-
Java正则表达式
2014-03-14 10:16 1719Java正则表达式详解 作者:jzj 文 ... -
类的初始化与清理
2013-06-24 22:20 1412初始化时内存清零 当创建一个对象时,首先将在堆上为这个对象分 ... -
protected,这个错了吗?
2013-06-24 22:17 1203这几天对protected修饰符有点迷糊,随便找同事要了一本 ... -
Java中BigDecimal的8种舍入模式
2013-06-21 18:42 2146java.math.BigDecimal不可变的、任意精度的 ... -
Tomcat性能参数设置
2010-12-27 15:35 34700默认参数不适合生产环境使用,因此需要修改一些参数 1、 ... -
Java 6 JVM参数选项大全
2010-12-14 11:16 1601http://kenwublog.com/docs/java6 ... -
对象的安全构造
2013-06-21 18:43 1511在构造期间,不要公布“this”引用 一种可以将数据争用引 ... -
Java断言(assert)—— 转
2010-06-20 10:36 12034一、概述 在C和C++语言中都有assert关键,表示断言。 ... -
eclipse调试
2010-06-04 00:11 8007eclipse远程调试 在eclipse3.4前,远程调试时 ... -
protected,你真的理解了吗?
2010-05-09 17:56 2091Java中的访问控制修饰符有四个级别,但属protected最 ... -
利用反射进行深层克隆
2010-05-05 21:02 3631最近在看《effective java ... -
类与类之间的几种关系
2010-05-03 13:49 2380类和类、类和接口、接 ... -
运行java
2010-05-03 13:47 1016用javac命令编译一个打包的类时,如果没有加参数" ... -
Java内存模型与volatile
2010-04-25 13:21 18520内存模型描述的是程序 ... -
中断线程
2010-04-24 21:19 8916中断线程 线程的thread.i ... -
java中的关键字、保留字、标示符
2010-04-07 23:48 3331关键字 Java的关键字对java的编译器有特殊的意义, ... -
Java中的浮点数剖析
2010-04-07 23:27 4662定点数表达法的缺点在于其形式过于僵硬,固定的小数点位置决定了固 ... -
线程间的同步与互斥
2010-03-23 21:29 2267线程间的同步(实指线程间的通信):一般来说,一个线程相对于另 ... -
UTF-16、UTF-16BE、UTF-16LE编码方式的区别
2010-03-23 21:20 9704import java.io.IOException; ... -
final、finally、finalize
2010-01-22 01:15 2342final关键字 先看看final关键字,它可以被用于以下几个 ...
相关推荐
正确理解ThreadLocal.pdf
理解ThreadLocal 理解ThreadLocal 理解ThreadLocal 理解ThreadLocal
ThreadLocal应用示例及理解,这个写了相关的示例,可以参考一下。
ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序,ThreadLocal并不是一个Thread,而是Thread的局部变量
并且由于每个线程在访问该变量时,读取和修改的,都是独有的那份变量拷贝,变量被彻底封闭在每个访问的线程中,并发错误出现的可能也完全消除了。} catch (SQL
ThreadLocal入门教程。 讲解了线程安全和ThreadLocal的使用的基本知识。
ThreadLocal
ThreadLocal深度理解
主要介绍了深入理解ThreadLocal工作原理及使用示例,涉及ThreadLocal<T> 简介和使用示例及ThreadLocal的原理等相关内容,具有一定参考价值,需要的朋友可以了解下。
Java中ThreadLocal工具类(解决多线程程序中并发问题的一种新思路,主要为参数的拷贝问题),感兴趣的话可以查看博文,博文地址:http://blog.csdn.net/otengyue/article/details/38459327
ThreadLocal的简单理解.doc
学习ThreadLocal,了解其中的原理,以及学习其中的优点!避免坑点!!
DbUTils中用ThreadLocal类
主要介绍ThreadLocal的原理,实例分析以及注意事项
java 简单的ThreadLocal示例
ThreadLocal的几种误区ThreadLocal的几种误区ThreadLocal的几种误区
Synchronized与ThreadLocal