A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 熊永标 中级黑马   /  2013-1-25 14:15  /  1287 人查看  /  1 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

  1. package cn.javastudy.p19.threadlocal.demo;

  2. import java.util.Timer;
  3. import java.util.TimerTask;

  4. public class ThreadDemo {

  5. /**
  6. * @param args
  7. */
  8. public static void main(String[] args) {
  9. //线程一:
  10. Thread t1=new Thread(new Runnable(){

  11. @Override
  12. public void run() {
  13. // TODO Auto-generated method stub
  14. Model1.set("张三", 25);
  15. Model1.get();
  16. }});
  17. t1.start();
  18. //线程二:
  19. Thread t2=new Thread(new Runnable(){

  20. @Override
  21. public void run() {
  22. // TODO Auto-generated method stub
  23. Model2.set("李四", 30);
  24. Model2.get();
  25. }});
  26. t2.start();
  27. }
  28. //根据线程获取对应的对象,然后操作对象
  29. static class Model1{
  30. public static void set(String name,int age)
  31. {
  32. ThreadLocalDemo.getThreadInstance().setName(name);
  33. ThreadLocalDemo.getThreadInstance().setAge(age);
  34. }
  35. public static void get()
  36. {
  37. System.err.println(Thread.currentThread().getName()+": name:"+ThreadLocalDemo.getThreadInstance().getName()+"age:"+ThreadLocalDemo.getThreadInstance().getAge());
  38. }
  39. }
  40. //根据线程获取对应的对象,然后操作对象
  41. static class Model2{
  42. public static void set(String name,int age)
  43. {
  44. ThreadLocalDemo.getThreadInstance().setName(name);
  45. ThreadLocalDemo.getThreadInstance().setAge(age);
  46. }
  47. public static void get()
  48. {
  49. System.err.println(Thread.currentThread().getName()+": name:"+ThreadLocalDemo.getThreadInstance().getName()+"age:"+ThreadLocalDemo.getThreadInstance().getAge());
  50. }
  51. }
  52. //线程的共享数据类
  53. static class ThreadLocalDemo{
  54. private static ThreadLocal<ThreadLocalDemo> threadLocal =new ThreadLocal<ThreadLocalDemo>();
  55. private ThreadLocalDemo(){}
  56. private static ThreadLocalDemo tld=null;
  57. private String name;
  58. private int age;
  59. public static ThreadLocalDemo getThreadInstance()
  60. {
  61. tld=threadLocal.get();
  62. if(tld==null)
  63. {
  64. tld=new ThreadLocalDemo();
  65. threadLocal.set(tld);
  66. }
  67. return tld;
  68. }

  69. public String getName() {
  70. return name;
  71. }
  72. public void setName(String name) {
  73. this.name = name;
  74. }
  75. public int getAge() {
  76. return age;
  77. }
  78. public void setAge(int age) {
  79. this.age = age;
  80. }
  81. }

  82. }
复制代码
ThreadLocal实现了线程的多个模块的数据的共享。使每个线程的各个数据独立存在,减少了线程的安全隐患。三根据线程的不同而自由切换。

评分

参与人数 1技术分 +1 收起 理由
冯海霞 + 1

查看全部评分

1 个回复

正序浏览
顾名思义它是local variable(线程局部变量)。它的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。
使用场景
To keep state with a thread (user-id, transaction-id, logging-id)
To cache objects which you need frequently
ThreadLocal类
它主要由四个方法组成initialValue(),get(),set(T),remove(),其中值得注意的是initialValue(),该方法是一个protected的方法,显然是为了子类重写而特意实现的。该方法返回当前线程在该线程局部变量的初始值,这个方法是一个延迟调用方法,在一个线程第1次调用get()或者set(Object)时才执行,并且仅执行1次。ThreadLocal中的确实实现直接返回一个null:
ThreadLocal的原理
ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单,在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。比如下面的示例实现:
public class ThreadLocal
{
 private Map values = Collections.synchronizedMap(new HashMap());
 public Object get()
 {
  Thread curThread = Thread.currentThread();
  Object o = values.get(curThread);
  if (o == null && !values.containsKey(curThread))
  {
   o = initialValue();
   values.put(curThread, o);
  }
  return o;
 }
 public void set(Object newValue)
 {
  values.put(Thread.currentThread(), newValue);
 }
 public Object initialValue()
 {
  return null;
 }
}
ThreadLocal 的使用
使用方法一:
Hibernate的文档时看到了关于使ThreadLocal管理多线程访问的部分。具体代码如下

1.  public static final ThreadLocal session = new ThreadLocal();
2.  public static Session currentSession() {
3.      Session s = (Session)session.get();
4.      //open a new session,if this session has none
5.   if(s == null){
6.      s = sessionFactory.openSession();
7.      session.set(s);
8.   }
      return s;
9. }

我们逐行分析
1。 初始化一个ThreadLocal对象,ThreadLocal有三个成员方法 get()、set()、initialvalue()。
    如果不初始化initialvalue,则initialvalue返回null。
3。session的get根据当前线程返回其对应的线程内部变量,也就是我们需要的net.sf.hibernate.Session(相当于对应每个数据库连接).多线程情况下共享数据库链接是不安全的。ThreadLocal保证了每个线程都有自己的s(数据库连接)。
5。如果是该线程初次访问,自然,s(数据库连接)会是null,接着创建一个Session,具体就是行6。
6。创建一个数据库连接实例 s
7。保存该数据库连接s到ThreadLocal中。
8。如果当前线程已经访问过数据库了,则从session中get()就可以获取该线程上次获取过的连接实例。
使用方法二
当要给线程初始化一个特殊值时,需要自己实现ThreadLocal的子类并重写该方法,通常使用一个内部匿名类对ThreadLocal进行子类化,EasyDBO中创建jdbc连接上下文就是这样做的:
public class JDBCContext{
private static Logger logger = Logger.getLogger(JDBCContext.class);
private DataSource ds;
protected Connection connection;
private boolean isValid = true;
private static ThreadLocal jdbcContext;

private JDBCContext(DataSource ds){
  this.ds = ds;
  createConnection();  
}
public static JDBCContext getJdbcContext(javax.sql.DataSource ds)
{  
  if(jdbcContext==null)jdbcContext=new JDBCContextThreadLocal(ds);
  JDBCContext context = (JDBCContext) jdbcContext.get();
  if (context == null) {
   context = new JDBCContext(ds);
  }
  return context;
}
private static class JDBCContextThreadLocal extends ThreadLocal {
  public javax.sql.DataSource ds;
  public JDBCContextThreadLocal(javax.sql.DataSource ds)
  {
   this.ds=ds;
  }
  protected synchronized Object initialValue() {
   return new JDBCContext(ds);
  }
}
}
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马