- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
这又是一道大厂经常问到的Java并发编程方面的面试热点!
ThreadLocal 主要是做数据隔离,它是线程的局部变量, 是每一个线程所单独持有的,其他线程不能对其进行访问,相对隔离的。
当使用ThreadLocal维护变量的时候为每一个使用该变量的线程提供一个独立的变量副本,即每个线程内部都会有一个该变量,这样同时多个线程访问该变量并不会彼此相互影响,因此他们使用的都是自己从内存中拷贝过来的变量的副本,这样就不存在线程安全问题,也不会影响程序的执行性能。
解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。
每个Thread对象都有一个ThreadLocalMap,当创建一个ThreadLocal的时候,就会将该ThreadLocal对象添加到该Map中,其中键就是ThreadLocal,值可以是任意类型。
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
* (将此线程局部变量的当前线程副本设置为指定值。 大多数子类将不需要覆盖此方法,仅依赖于initialValue方法来设置线程initialValue的值)
*
* @param value the value to be stored in the current thread's copy of this thread-local.
* (要存储在此线程本地的当前线程副本中的值。)
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
* (返回此线程局部变量的当前线程副本中的值。 如果该变量对于当前线程没有值,则首先将其初始化为调用initialValue方法返回的值。)
*
* @return the current thread's value of this thread-local(此线程本地的当前线程的值)
*/
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();
}
解读:首先看“set”方法,它先获取当前线程,然后获取线程的ThreadLocalMap对象。校验对象是为空,不为空时set,否则创建一个map对象。
关注一下第2行“getMap”,ThreadLocalMap是当前线程Thread中获取的,来源于Thread中⼀个叫threadLocals的变量。
/**
* Get the map associated with a ThreadLocal. Overridden in InheritableThreadLocal.
* (获取与 ThreadLocal 关联的map。在InheritableThreadLocal中重写)
*
* @param t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. (与此线程有关的 ThreadLocal 值。该map由 ThreadLocal 类维护)*/
ThreadLocal.ThreadLocalMap threadLocals = null;
看到这里,我们应该明白ThreadLocal数据隔离原理了,每个线程Thread都维护了自己的threadLocals变量,这个是独立的,别人访问不到的,从而实现了隔离。
ThreadLocalMap是一个定制化的Hashmap。但是没有链表。
在插⼊过程中,根据ThreadLocal对象的hash值,定位到table中的位置i。如果当前位置是空的,就初始化⼀个Entry对象放在位置i上;如果位置i不为空,如果这个Entry对象的key和要设置的key相等,那么就刷新Entry中的value;如果位置i的不为空,而且key不等于entry,那就找下⼀个空位置,直到为空为⽌。这样的话,在get的时候,也会根据ThreadLocal对象的hash值,定位到table中的位置,然后判断该位置Entry对象中的key是否和get的key⼀致,如果不⼀致,就判断下⼀个位置,在冲突严重的情况下,效率会比较低。
在Java中,栈内存归属于单个线程,每个线程都会有一个栈内存,其存储的变量只能在其所属线程中可见,即栈内存可以理解成线程的私有内存。而堆内存中的对象对所有线程可见。堆内存中的对象可以被所有线程访问。
所以,是存放在栈上咯?
然而并不是,因为ThreadLocal实例实际上也是被其创建的类持有(更顶端应该是被线程持有),而ThreadLocal的值其实也是被线程实例持有,它们都是位于堆上,只是通过⼀些技巧将可⻅性修改成了线程可见。
使⽤ InheritableThreadLocal 可以实现多个线程访问ThreadLocal的值,我们在主线程中创建⼀个 InheritableThreadLocal 的实例,然后在⼦线程中得到这个 InheritableThreadLocal 实例设置的值。
最常见的ThreadLocal使用场景为用来解决数据库连接、Session管理等。
// 数据库连接
private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() {
public Connection initialValue() {
return DriverManager.getConnection(DB_URL);
}
};
public static Connection getConnection() {
return connectionHolder.get();
}
// Session管理
private static final ThreadLocal threadSession = new ThreadLocal();
public static Session getSession() throws InfrastructureException {
Session s = (Session) threadSession.get();
try {
if (s == null) {
s = getSessionFactory().openSession();
threadSession.set(s);
}
} catch (HibernateException ex) {
throw new InfrastructureException(ex);
}
return s;
}
实际上 ThreadLocalMap 中使用的 key 为 ThreadLocal 的弱引用,弱引用的特点是,如果这个对象只存在弱引用,那么在下一次垃圾回收的时候必然会被清理掉。所以如果 ThreadLocal 没有被外部强引用的情况下,在垃圾回收的时候会被清理掉的,这样一来 ThreadLocalMap中使用这个 ThreadLocal 的 key 也会被清理掉。但是,value 是强引用,不会被清理,这样一来就会出现 key 为 null 的 value。
ThreadLocalMap实现中已经考虑了这种情况,在调用 set()、get()、remove() 方法的时候,会清理掉 key 为 null 的记录。如果说会出现内存泄漏,那只有在出现了 key 为 null 的记录后,没有手动调用 remove() 方法,并且之后也不再调用 get()、set()、remove() 方法的情况下。
建议回收自定义的ThreadLocal变量,尤其在线程池场景下,线程经常会被复用,如果不清理自定义的 ThreadLocal变量,可能会影响后续业务逻辑和造成内存泄露等问题。 尽量在代理中使用try-finally块进行回收。
我的 Django 应用程序在生产过程中变得非常缓慢。可能是由于某些复杂或未索引的查询。 是否有任何类似 django 的方法来分析我的应用程序? 最佳答案 试试 Django Debug Toolb
我正在使用GDownloadUrl将客户端数据发送到服务器。这里是我使用的sode GDownloadUrl( dwnld_url, function(data) {
我一直在尝试开始分析我的 CherryPy 网络服务器,但文档缺乏关于如何设置它的详细信息。我知道我应该能够使用 cherrypy.lib.profiler 作为中间件来安装我的初始服务器。现在,我有
是否有任何 HashMap 实现公开了用于分析 Map 性能的钩子(Hook)方法(平均链长度、最佳/最差/平均访问时间、#rehashes 等)。 在 ~O(1) 访问时间方面使用 HashMap
我想知道是否有可用的技术或工具可以告诉您执行特定方法需要多少时间。 类似于数学/计算机科学中的大 O 符号,可以让您了解算法的复杂性,我想知道代码分析是否有类似的东西。 最佳答案 Profiling是
前面,我们说Ruby没有函数,只有方法.而且实际上有不止一种方法.这一节我们介绍访问控制(accesscontrols). 想想当我们在"最高层"而不是在一个类的定义里定义一个
我有一个用 Visual Basic 编写的大型应用程序6,我需要分析它。有没有类似 ANTS Profiler 的东西但对于 COM应用程序? 最佳答案 我以前用 VBWatch .我对它有很好的体
我做了两个实现来解决 Shikaku 难题。一个使用顶部、左侧、宽度和高度 (TLWH) 作为每个矩形的参数,另一个使用顶部、左侧、底部、右侧 (TLBR)。 出于某种原因,使用 TLBR 的速度要快
如果重要的话,我正在使用 Very Sleepy CS 进行分析。 IPP(Intel's Integrated Performance Primitives)在我的项目中使用,基本上是单次调用: i
我想弄明白这个排列算法是如何工作的: def perm(n, i): if i == len(n) - 1: print n else: for j i
如果 C++ 项目的物理结构有利于编译速度,您将如何获得某种客观的衡量标准?有些人说你应该避免使用模板以获得更好的编译速度,但是如果模板生成大量非冗余目标代码,即使行/秒编译速度不是很好,那也不是很糟
摘自 Robert Sedgewick 和 Kevin Wayne 算法第 4 版 在递归部分基本情况代码是 if(end start) { mid = (start+end
有没有一种简单的方法可以计算一段标准 C 代码实际执行的乘法次数?我想到的代码基本上只是做加法和乘法,主要兴趣是乘法,但也可以计算其他操作的数量。 如果这是一个选项,我想我可以四处用“multiply
我正在编写一个 Netty 应用程序。该应用程序运行在 64 位八核 linux 机器上 Netty 应用程序是一个简单的路由器,它接受请求(传入管道),从请求中读取一些元数据并将数据转发到远程服务(
我希望能得到一些帮助来弄清楚这个异常消息到底对我说了什么。我能够使用调试器将问题缩小到代码中的特定行。但是,我认为更多信息可以更好地找出代码中的实际问题。 public static List
我有一个存储在 USB 拇指驱动器上的 mysql 数据库,该数据库已不可挽回地丢失了其文件分配表。因此,我无法访问整个 ibdata1 文件。不过,我可以找到使用十六进制编辑器使用的记录页面。 所有
我正在使用 jQuery 在单击时显示图像。通过将图像源存储到像这样的变量中,这可以很好地工作.. var theSrc = $(event.target).attr('src'); 然后我可以将这张
我是 R 的新手,但不是 C 的新手。我想看看是否可以为 friend 加速一个包。通常使用 C 我会编译一个设置了 -pg 标志的应用程序,然后将 gmon.out 文件传递给 gprof。 我
在分析我的代码以找出运行缓慢的地方时,我有 3 个功能显然会永远占用,这就是非常困的说法。 这些函数是: ZwDelayExecution 20.460813 20.460813 19.
我正在分析一个应用程序并注意到 52% (195MB) 的内存被 char[] 使用,20% 被 String 使用。这是一个有很多依赖项的大型项目,我刚刚看到它,所以我有几个相关的问题可以帮助我开始
我是一名优秀的程序员,十分优秀!