首页IT科技函数days360是什么意思(day36-ThreadLocal)

函数days360是什么意思(day36-ThreadLocal)

时间2025-05-05 12:40:10分类IT科技浏览3471
导读:ThreadLocal 线程数据共享和安全...

ThreadLocal

线程数据共享和安全

1.什么是ThreadLocal?

ThreadLocal的作用            ,可以实现在同一个线程数据共享               ,从而解决多线程数据安全问题

当http请求发送到Tomcat服务端时      ,Tomcat会创建一个线程去处理这个http请求         ,如果是请求servlet               ,servlet可能又会调用其他service         ,在这些service中      ,又可能会调用dao               ,去对数据库进行操作            。

在这些资源或者方法的调用中           ,为解决数据安全问题   ,在这一个线程执行的过程中                ,我们希望有一个数据是共享的             ,而且是安全的               。

应用场景:比如说事务控制,一个线程可能涉及到多个service的调用              ,调用多个dao               ,在这过程中   ,可能对数据库的多张表进行了操作      。这时我们希望在整个业务流程结束之后            ,再进行一次提交commit         。反过来说               ,在没有进行提交之前      ,我们希望始终是一个connection在操作         ,这样才能在结束时进行统一的一次提交(在开始操作的时候将自动提交设置为false)               。

这样就可以解决同一个请求中               ,调用多个service或者多个dao的需求         ,这个需求也是开发中必须解决的事务安全问题(事务一致性需求)         。

ThreadLocal技术就能够很好地解决这个问题      。我们可以在实际开发中使用Filter和ThreadLocal解决事务安全问题               。

一个ThreadLocal对象可以给当前线程关联一个数据(普通变量      ,对象               ,对组)--使用set方法

ThreadLocal可以像Map一样存取数据           ,key为当前的ThreadLocal对象--使用get方法

每一个ThreadLocal对象只能为当前线程关联一个数据   ,如果要为当前线程关联多个数据                ,就需要使用多个ThreadLocal对象实例

每个ThreadLocal对象实例定义的时候             ,一般为static类型

ThreadLocal中保存的数据,在线程销毁之后              ,会自动释放

2.ThreadLocal快速入门

2.1ThreadLocal的类图

如下:ThreadLocal类中常用的方法有get()               ,set()   ,getMap()等            ,ThreadLocal类中含有一个重要的内部类ThreadLocalMap               ,ThreadLocalMap类中又含有一个内部类Entry      ,数据以key-value的形式存放在Entry中           。

ThreadLocal核心的价值就是:在一个线程中         ,以线程安全的方式来共享数据   。

2.2应用实例

需求: 演示 ThreadLocal (作用:在一个线程中, 共享数据(线程安全))的使用

Dog:

package com.li.threadlocal; public class Dog { }

T1:

package com.li.threadlocal; public class T1 { //创建ThreadLocal对象, public static修饰 public static ThreadLocal<Object> threadLocal1 = new ThreadLocal<>(); public static void main(String[] args) { new Thread(new Task()).start();//启动一个新的线程               ,注意不是主线程 } //Task是一个线程类         ,同时是一个内部类 public static class Task implements Runnable { @Override public void run() { Dog dog = new Dog(); Pig pig = new Pig(); //给threadLocal1对象放入dog System.out.println("Task 放入了 dog=" + dog); threadLocal1.set(dog); System.out.println("Task 的 run 方法中的线程= " + Thread.currentThread().getName()); new T1Service().update(); } } }

T1Service:

package com.li.threadlocal; public class T1Service { public void update() { //取出threadLocal1对象关联的对象 Object o = T1.threadLocal1.get(); //获取当前线程名 String name = Thread.currentThread().getName(); System.out.println("在T1Service 的update()的线程是= " + name + ", 取出dog= " + o); //调用了dao-update()方法 new T1DAO().update(); } }

T1DAO:

package com.li.threadlocal; public class T1DAO { public void update() { //取出线程关联的threadLocal1对象的数据 Object o = T1.threadLocal1.get(); //获取当前线程的名称 String name = Thread.currentThread().getName(); System.out.println("在T1DAO 的update()的线程是= " + name + ", 取出dog= " + o); } }

可以看到所有方法中拿到的对象都是同一个:

3.源码分析

3.1ThreadLocal的set()

在2.2的应用实例中      ,我们在T1类中使用了ThreadLocal.set()方法               ,现在来看看set()方法的底层源码:

set()方法关联的其他方法和属性:

从上述代码中我们可以知道set方法的主要工作如下:

public void set(T value) { //1.获取当前线程 Thread t = Thread.currentThread(); //2.通过线程对象           ,获取到和此线程关联的ThreadLocalMap // (ThreadLocalMap是ThreadLocal里的一个静态内部类*, // 类型是ThreadLocal$ThreadLocalMap) ThreadLocalMap map = getMap(t); //3.如果获取到的ThreadLocalMap不为空   ,就将传入的数据放入map                ,其中: // -key为当前的ThreadLocal对象(this) -value为存放的数据             , // 因为key值不能重复(map性质),一个ThreadLocal对象只能存放一个数据 // 如果再赋值              ,就会替换旧的value值 if (map != null) map.set(this, value); //4.如果和当前线程关联的ThreadLocalMap为null               , //就创建一个和当前线程关联的ThreadLocalMap   ,并且将存放的数据作为value放入map            , //这里的key为当前线程t(作用是让线程和创建的map关联起来) else createMap(t, value); }

在2.2应用实例的threadLocal1.set(dog);语句旁打上断点               ,点击debug      ,点击step over                。

如下图所示         ,可以看到子线程Thread-0中有一个threadLocals属性(该属性的类型为ThreadLocalMap)               ,该map中又有一个table属性(table的类型为Entry[]数组), table数组存放Entry对             。

这里涉及到的弱引用暂不深入

在Entry对中         ,以k-v的形式存放数据      ,key值为 当前的线程中 的ThreadLocal对象               ,value值为存放的数据。

因为map的存放性质           ,如果在同一个ThreadLocal对象中存放多个value   ,那么在底层的Entry对中保存的是最近存放的value值                ,这也是为什么一个ThreadLocal对象只能存放一个值              。

线程中所有的ThreadLocal对象都被当前线程的threadLocals属性(map)管理               。因此无论在哪个方法中             ,只要能找到对应的线程Thread,就对该线程关联的所有ThreadLocal对象中的value值进行操作   。

一个线程中可以有多个ThreadLocal对象              ,如果还有其他ThreadLocal对象               ,使用set方法   ,存放的就是其他Entry对(key值就是其他的ThreadLocal对象)

存放多个ThreadLocal对象:

3.2ThreadLocal的get()

public T get() { //先得到当前的线程对象 Thread t = Thread.currentThread(); //获取和当前线程对象关联的ThreadLocalMap ThreadLocalMap map = getMap(t); //如果map不为空 if (map != null) { //就根据当前的调用者this(即当前调用get方法的ThreadLocal对象)            ,得到对应的Entry ThreadLocalMap.Entry e = map.getEntry(this); //如果Entry的值e不为空 if (e != null) { @SuppressWarnings("unchecked") //返回当前ThreadLocal对象关联的value值 T result = (T)e.value; return result; } } //如果map为空               ,就初始化map      ,并将map和当前线程关联 return setInitialValue(); }

3.3总结

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
织梦如何添加浮动广告(dedecms织梦自定义表单地区联动类型不可用的解决方案) 网站快速收录技巧(推广让网站更快速被搜索引擎收录)