本篇文章给大家带来的内容是关于java中threadlocal的详细介绍(代码示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。
threadlocal基本在项目开发中基本不会用到, 但是面试官是比较喜欢问这类问题的;所以还是有必要了解一下该类的功能与原理的.
threadlocal是什么
threadlocal是一个将在多线程中为每一个线程创建单独的变量副本的类; 当使用threadlocal来维护变量时, threadlocal会为每个线程创建单独的变量副本, 避免因多线程操作共享变量而导致的数据不一致的情况;
threadlocal类用在哪些场景一般来说, threadlocal在实际工业生产中并不常见, 但是在很多框架中使用却能够解决一些框架问题; 比如spring中的事务、spring 中 作用域 scope 为 request的bean 使用threadlocal来解决.
threadlocal使用方法1、将需要被多线程访问的属性使用threadlocal变量来定义; 下面以网上多数举例的dbconnectionfactory类为例来举例
import java.sql.connection;import java.sql.drivermanager;import java.sql.sqlexception;public class dbconnectionfactory { private static final threadlocal<connection> dbconnectionlocal = new threadlocal<connection>() { @override protected connection initialvalue() { try { return drivermanager.getconnection(, , ); } catch (sqlexception e) { e.printstacktrace(); } return null; } }; public connection getconnection() { return dbconnectionlocal.get(); }}
这样在client获取connection的时候, 每个线程获取到的connection都是该线程独有的, 做到connection的线程隔离; 所以并不存在线程安全问题
threadlocal如何实现线程隔离1、主要是用到了thread对象中的一个threadlocalmap类型的变量threadlocals, 负责存储当前线程的关于connection的对象, 以dbconnectionlocal 这个变量为key, 以新建的connection对象为value; 这样的话, 线程第一次读取的时候如果不存在就会调用threadlocal的initialvalue方法创建一个connection对象并且返回;
具体关于为线程分配变量副本的代码如下:
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();}
1、首先获取当前线程对象t, 然后从线程t中获取到threadlocalmap的成员属性threadlocals
2、如果当前线程的threadlocals已经初始化(即不为null) 并且存在以当前threadlocal对象为key的值, 则直接返回当前线程要获取的对象(本例中为connection);
3、如果当前线程的threadlocals已经初始化(即不为null)但是不存在以当前threadlocal对象为key的的对象, 那么重新创建一个connection对象, 并且添加到当前线程的threadlocals map中,并返回
4、如果当前线程的threadlocals属性还没有被初始化, 则重新创建一个threadlocalmap对象, 并且创建一个connection对象并添加到threadlocalmap对象中并返回。
如果存在则直接返回很好理解, 那么对于如何初始化的代码又是怎样的呢?
private t setinitialvalue() { t value = initialvalue(); thread t = thread.currentthread(); threadlocalmap map = getmap(t); if (map != null) map.set(this, value); else createmap(t, value); return value;}
1、首先调用我们上面写的重载过后的initialvalue方法, 产生一个connection对象
2、继续查看当前线程的threadlocals是不是空的, 如果threadlocalmap已被初始化, 那么直接将产生的对象添加到threadlocalmap中, 如果没有初始化, 则创建并添加对象到其中;
同时, threadlocal还提供了直接操作thread对象中的threadlocals的方法
public void set(t value) { thread t = thread.currentthread(); threadlocalmap map = getmap(t); if (map != null) map.set(this, value); else createmap(t, value);}
这样我们也可以不实现initialvalue, 将初始化工作放到dbconnectionfactory的getconnection方法中:
public connection getconnection() { connection connection = dbconnectionlocal.get(); if (connection == null) { try { connection = drivermanager.getconnection(, , ); dbconnectionlocal.set(connection); } catch (sqlexception e) { e.printstacktrace(); } } return connection;}
那么我们看过代码之后就很清晰的知道了为什么threadlocal能够实现变量的多线程隔离了; 其实就是用了map的数据结构给当前线程缓存了, 要使用的时候就从本线程的threadlocals对象中获取就可以了, key就是当前线程;
当然了在当前线程下获取当前线程里面的map里面的对象并操作肯定没有线程并发问题了, 当然能做到变量的线程间隔离了;
现在我们知道了threadlocal到底是什么了, 又知道了如何使用threadlocal以及其基本实现原理了是不是就可以结束了呢? 其实还有一个问题就是threadlocalmap是个什么对象, 为什么要用这个对象呢?
threadlocalmap对象是什么本质上来讲, 它就是一个map, 但是这个threadlocalmap与我们平时见到的map有点不一样
1、它没有实现map接口;
2、它没有public的方法, 最多有一个default的构造方法, 因为这个threadlocalmap的方法仅仅在threadlocal类中调用, 属于静态内部类
3、threadlocalmap的entry实现继承了weakreference<threadlocal<?>>
4、该方法仅仅用了一个entry数组来存储key, value; entry并不是链表形式, 而是每个bucket里面仅仅放一个entry;
以上就是java中threadlocal的详细介绍(代码示例)的详细内容。
