早在JDK 1.2的版本中就提供Java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。

  当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

  从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。

  所以,在Java中编写线程局部变量的代码相对来说要笨拙一些,因此造成线程局部变量没有在Java开发者中得到很好的普及。

ThreadLocal的接口方法

ThreadLocal类接口很简单,只有4个方法,我们先来了解一下:

  • void set(Object value)设置当前线程的线程局部变量的值。
  • public Object get()该方法返回当前线程所对应的线程局部变量。
  • public void remove()将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。
  • protected Object initialValue()返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。

  值得一提的是,在JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal<T>。API方法也相应进行了调整,新版本的API方法分别是void set(T value)、T get()以及T initialValue()。

  ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。我们自己就可以提供一个简单的实现版本:

  1. package com.test;
  2. public class TestNum {
  3. // ①通过匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值
  4. private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>() {
  5. public Integer initialValue() {
  6. return 0;
  7. }
  8. };
  9. // ②获取下一个序列值
  10. public int getNextNum() {
  11. seqNum.set(seqNum.get() + 1);
  12. return seqNum.get();
  13. }
  14. public static void main(String[] args) {
  15. TestNum sn = new TestNum();
  16. // ③ 3个线程共享sn,各自产生序列号
  17. TestClient t1 = new TestClient(sn);
  18. TestClient t2 = new TestClient(sn);
  19. TestClient t3 = new TestClient(sn);
  20. t1.start();
  21. t2.start();
  22. t3.start();
  23. }
  24. private static class TestClient extends Thread {
  25. private TestNum sn;
  26. public TestClient(TestNum sn) {
  27. this.sn = sn;
  28. }
  29. public void run() {
  30. for (int i = 0; i < 3; i++) {
  31. // ④每个线程打出3个序列值
  32. System.out.println("thread[" + Thread.currentThread().getName() + "] --> sn["
  33. + sn.getNextNum() + "]");
  34. }
  35. }
  36. }
  37. }
 

通常我们通过匿名内部类的方式定义ThreadLocal的子类,提供初始的变量值,如例子中①处所示。TestClient线程产生一组序列号,在③处,我们生成3个TestClient,它们共享同一个TestNum实例。运行以上代码,在控制台上输出以下的结果:

thread[Thread-0] --> sn[1]
thread[Thread-1] --> sn[1]
thread[Thread-2] --> sn[1]
thread[Thread-1] --> sn[2]
thread[Thread-0] --> sn[2]
thread[Thread-1] --> sn[3]
thread[Thread-2] --> sn[2]
thread[Thread-0] --> sn[3]
thread[Thread-2] --> sn[3]

考察输出的结果信息,我们发现每个线程所产生的序号虽然都共享同一个TestNum实例,但它们并没有发生相互干扰的情况,而是各自产生独立的序列号,这是因为我们通过ThreadLocal为每一个线程提供了单独的副本。

ThreadLocal是什么?保证线程安全的更多相关文章

  1. 在JAVA中ArrayList如何保证线程安全

    [b]保证线程安全的三种方法:[/b]不要跨线程访问共享变量使共享变量是final类型的将共享变量的操作加上同步一开始就将类设计成线程安全的, 比在后期重新修复它,更容易.编写多线程程序, 首先保证它 ...

  2. Java中ThreadLocal无锁化线程封闭实现原理

    虽然现在可以说很多程序员会用ThreadLocal,但是我相信大多数程序员还不知道ThreadLocal,而使用ThreadLocal的程序员大多只是知道其然而不知其所以然,因此,使用ThreadLo ...

  3. ASP.NET MVC Filters 4种默认过滤器的使用【附示例】 数据库常见死锁原因及处理 .NET源码中的链表 多线程下C#如何保证线程安全? .net实现支付宝在线支付 彻头彻尾理解单例模式与多线程 App.Config详解及读写操作 判断客户端是iOS还是Android,判断是不是在微信浏览器打开

    ASP.NET MVC Filters 4种默认过滤器的使用[附示例]   过滤器(Filters)的出现使得我们可以在ASP.NET MVC程序里更好的控制浏览器请求过来的URL,不是每个请求都会响 ...

  4. EF 保证线程内唯一 上下文的创建

    1.ef添加完这个对象,就会自动返回这个对象数据库的内容,比如下面这个表是自增ID 最后打印出来的ID  就是自增的结果 2.lambda 中怎么select * var userInfoList = ...

  5. 多线程下C#如何保证线程安全?

    多线程编程相对于单线程会出现一个特有的问题,就是线程安全的问题.所谓的线程安全,就是如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码.如果每次运行结果和单线程运行的结果是 ...

  6. java中volatile不能保证线程安全

    今天打了打代码研究了一下java的volatile关键字到底能不能保证线程安全,经过实践,volatile是不能保证线程安全的,它只是保证了数据的可见性,不会再缓存,每个线程都是从主存中读到的数据,而 ...

  7. 最近面试被问到一个问题,AtomicInteger如何保证线程安全?

    最近面试被问到一个问题,AtomicInteger如何保证线程安全?我查阅了资料 发现还可以引申到 乐观锁/悲观锁的概念,觉得值得一记. 众所周知,JDK提供了AtomicInteger保证对数字的操 ...

  8. OSSpinLockLock加锁机制,保证线程安全并且性能高

    在aspect_add.aspect_remove方法里面用了aspect_performLocked, 而aspect_performLocked方法用了OSSpinLockLock加锁机制,保证线 ...

  9. Qunar机票技术部就有一个全年很关键的一个指标:搜索缓存命中率,当时已经做到了>99.7%。再往后,每提高0.1%,优化难度成指数级增长了。哪怕是千分之一,也直接影响用户体验,影响每天上万张机票的销售额。 在高并发场景下,提供了保证线程安全的对象、方法。比如经典的ConcurrentHashMap,它比起HashMap,有更小粒度的锁,并发读写性能更好。线程安全的StringBuilder取代S

    Qunar机票技术部就有一个全年很关键的一个指标:搜索缓存命中率,当时已经做到了>99.7%.再往后,每提高0.1%,优化难度成指数级增长了.哪怕是千分之一,也直接影响用户体验,影响每天上万张机 ...

随机推荐

  1. C# 控制datagridview的combox属性的列绑定数据

    //datagridvie列绑定list的数据 List<User> listChange = GetChange();//查询数据库内容,保存到list this.datagridvie ...

  2. 包管理工具Carthage使用

    iOS项目中第三方开源库的工具有Cocoapods和Carthage,swift官方出了一个包管理工具SPM(Swift Package Manager). 首先,大体讲一下Cocoapods和Car ...

  3. [原创]Hadoop默认设置导致NameNode启动失败一例

    看到市面上很多书在讲解Hadoop的时候都轻描淡写的提到了HDFS的设置问题.大多采取的是默认设置,最多也就是设置一些副本数量之类. 笔者在工作中遇到了这样一种情况:每次重启系统之后,NameNode ...

  4. JAVASE02-Unit06: 文件操作——File 、 文件操作—— RandomAccessFile

    Unit06: 文件操作--File . 文件操作-- RandomAccessFile java.io.FileFile的每一个实例是用来表示文件系统中的一个文件或目录 package day06; ...

  5. 日志监控系统中,大批量查询mysql方案

    最近开发遇到一个问题:需要查询一个大时间段内的数据,分1000个小段,即为1000个点.X轴是时间,Y轴是该小时间段内统计后数据.注意:数据返回是一个list,其中每个对象返回值都是该小时间段内数据统 ...

  6. SQLite - TRUNCATE TABLE

    https://www.tutorialspoint.com/sqlite/sqlite_truncate_table.htm Unfortunately, no TRUNCATE TABLE in ...

  7. Javascript ES6

    ES6: EMACScript 6 http://javascript.ruanyifeng.com/advanced/ecmascript6.html http://es6.ruanyifeng.c ...

  8. Git(分布式版本控制系统)在Windows下的使用-将代码托管到开源中国(oschina)

    一.Git是什么?     Git --- The stupid content tracker, 傻瓜内容跟踪器.Git是目前世界上最先进的分布式版本控制系统. 二.SVN与Git的最主要的区别?  ...

  9. jQuery .attr("checked")得undefined 问题解决

    出现此错误是因为JQuery版本升级的问题.所以此处应该使用.prop(); 那么,什么时候使用attr(),什么时候使用prop()?1.添加属性名称该属性就会生效应该使用prop();2.是有tr ...

  10. WebForm Repeater: 重复器

    Repeater控件,可以用来一次显示一组数据项.比如,可以用它们显示一个数据表中的所有行.             Repeater控件完全由模板驱动,提供了最大的灵活性,可以任意设置它的输出格式. ...