首页IT科技java多线程程序设计实验报告(Java多线程(4):ThreadLocal)

java多线程程序设计实验报告(Java多线程(4):ThreadLocal)

时间2025-08-05 05:06:15分类IT科技浏览5161
导读:您好,我是湘王,这是我的博客园,欢迎您来,欢迎您再来~...

您好               ,我是湘王                     ,这是我的博客园      ,欢迎您来           ,欢迎您再来~

为了提高CPU的利用率                      ,工程师们创造了多线程               。但是线程们说:要有光!(为了减少线程创建(T1启动)和销毁(T3切换)的时间)         ,于是工程师们又接着创造了线程池ThreadPool                     。就这样就可以了吗——不       ,工程师们并不满足于此                       ,他们不把自己创造出来的线程给扒个底朝天决不罢手      。

有了线程关键字解决线程安全问题            ,有了线程池解决效率问题   ,那还有什么问题是可以需要被解决的呢——还真被这帮疯子攻城狮给找到了!

当多个线程共享同一个资源的时候                       ,为了保证线程安全                ,有时不得不给资源加锁,例如使用Synchronized关键字实现同步锁           。这本质上其实是一种时间换空间的搞法——用单一资源让不同的线程依次访问                   ,从而实现内容安全可控                      。就像这样:

但是                    ,可以不可以反过来   ,将资源拷贝成多份副本的形式来同时访问               ,达到一种空间换时间的效果呢?当然可以                     ,就像这样:

而这      ,就是ThreadLocal最核心的思想         。

但这种方式在很多应用级开发的场景中用得真心不多           ,而且有些公司还禁止使用ThreadLocal                      ,因为它搞不好还会带来一些负面影响       。

其实         ,从拷贝若干副本这种功能来看       ,ThreadLocal是实现了在线程内部存储数据的能力的                       ,而且相互之间还能通信                       。就像这样:

还是以代码的形式来解读一下ThreadLocal            。有一个资源类Resource:

分别有ResuorceUtils1               、ResuorceUtils2ResuorceUtils3分别以不同的方式来连接资源            ,那么看看效率如何   。

/** * 连接资源工具类   ,通过静态方式获得连接 * * @author 湘王 */ public class ResourceUtils1 { // 定义一个静态连接资源 private static Resource resource = null; // 获取连接资源 public static Resource getResource() { if(resource == null) { resource = new Resource("xiangwang", "123456"); } return resource; } // 关闭连接资源 public static void closeResource() { if(resource != null) { resource = null; } } } /** * 连接资源工具类                       ,通过实例化方式获得连接 * * @author 湘王 */ public class ResourceUtils2 { // 定义一个连接资源 private Resource resource = null; // 获取连接资源 public Resource getResource() { if(resource == null) { resource = new Resource("xiangwang", "123456"); } return resource; } // 关闭连接资源 public void closeResource() { if(resource != null) { resource = null; } } } /** * 连接资源工具类                ,通过线程中的static Connection的副本方式获得连接 * * @author 湘王 */ public class ResourceUtils3 { // 定义一个静态连接资源 private static Resource resource = null; private static ThreadLocal<Resource> resourceContainer = new ThreadLocal<Resource>(); // 获取连接资源 public static Resource getResource() { synchronized(ResourceManager.class) { resource = resourceContainer.get(); if(resource == null) { resource = new Resource("xiangwang", "123456"); resourceContainer.set(resource); } return resource; } } // 关闭连接资源 public static void closeResource() { if(resource != null) { resource = null; resourceContainer.remove(); } } } /** * 连接资源管理类 * * @author 湘王 */ public class ResourceManager { public void insert() { // 获取连接 // System.out.println("Dao.insert()-->" + Thread.currentThread().getName() + ResourceUtils1.getResource()); // Resource resource = new ResourceUtils2().getResource(); Resource resource = ResourceUtils3.getResource(); System.out.println("Dao.insert()-->" + Thread.currentThread().getName() + resource); } public void delete() { // 获取连接 // System.out.println("Dao.delete()-->" + Thread.currentThread().getName() + ResourceUtils1.getResource()); // Resource resource = new ResourceUtils2().getResource(); Resource resource = ResourceUtils3.getResource(); System.out.println("Dao.delete()-->" + Thread.currentThread().getName() + resource); } public void update() { // 获取连接 // System.out.println("Dao.update()-->" + Thread.currentThread().getName() + ResourceUtils1.getResource()); // Resource resource = new ResourceUtils2().getResource(); Resource resource = ResourceUtils3.getResource(); System.out.println("Dao.update()-->" + Thread.currentThread().getName() + resource); } public void select() { // 获取连接 // System.out.println("Dao.select()-->" + Thread.currentThread().getName() + ResourceUtils1.getResource()); // Resource resource = new ResourceUtils2().getResource(); Resource resource = ResourceUtils3.getResource(); System.out.println("Dao.select()-->" + Thread.currentThread().getName() + resource); } public void close() { ResourceUtils3.closeResource(); } public static void main(String[] args) { for (int i = 0; i < 3; i++) { new Thread(new Runnable() { ResourceManager rm = new ResourceManager(); @Override public void run() { rm.insert(); rm.delete(); rm.update(); rm.select(); rm.close(); } }).start(); } } }

执行ResourceManager类中的main()方法后,可以清楚地看到:

第一种静态方式:大部分资源都能复用                   ,但毫无规律;

第二种实例方式:即使是同一个线程                    ,资源实例也不一样;

第三种ThreadLocal静态方式:相同的线程有相同的实例                       。

结论是:ThreadLocal实现了线程的资源复用                。

也可以通过画图的方式来看清楚三者之间的不同:

这是静态方式下的资源管理:

这是实例方式下的资源管理:

这是ThreadLocal静态方式下的资源管理:

理解了之后   ,再来看一个数据传递的例子               ,也就是ThreadLocal实现线程间通信的例子:

/** * 数据传递 * * @author 湘王 */ public class DataDeliver { static class Data1 { public void process() { Resource resource = new Resource("xiangwang", "123456"); //将对象存储到ThreadLocal ResourceContextHolder.holder.set(resource); new Data2().process(); } } static class Data2 { public void process() { Resource resource = ResourceContextHolder.holder.get(); System.out.println("Data2拿到数据: " + resource.getName()); new Data3().process(); } } static class Data3 { public void process() { Resource resource = ResourceContextHolder.holder.get(); System.out.println("Data3拿到数据: " + resource.getName()); } } static class ResourceContextHolder { public static ThreadLocal<Resource> holder = new ThreadLocal<>(); } public static void main(String[] args) { new Data1().process(); } }

运行代码之后                     ,可以看到Data1的数据都被Data2Data3拿到了      ,就像这样:

ThreadLocal在实际应用级开发中较少使用           ,因为容易造成OOM:

1                     、由于ThreadLocal是一个弱引用(WeakReference<ThreadLocal<?>>)                      ,因此会很容易被GC回收;

2      、ThreadLocalMap的生命周期和Thread相同         ,这就会造成当key=null时       ,value却还存在                       ,造成内存泄漏。所以            ,使用完ThreadLocal后需要显式调用remove操作(但很多码农不知道这一点)                   。

感谢您的大驾光临!咨询技术           、产品                      、运营和管理相关问题   ,请关注后留言                    。欢迎骚扰                       ,不胜荣幸~

声明:本站所有文章                ,如无特殊说明或标注,均为本站原创发布   。任何个人或组织                   ,在未征得本站同意时                    ,禁止复制         、盗用       、采集                       、发布本站内容到任何网站            、书籍等各类媒体平台               。如若本站内容侵犯了原著者的合法权益   ,可联系我们进行处理                     。

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

展开全文READ MORE
apsara clouder云认证答案(Apsara Clouder云计算专项技能认证:云服务器ECS入门[考试真题分享])