论坛首页 入门技术论坛

JVM 唯一,还是每个thread一个?有关 ThreadLocal 生成实例数量的困惑。

浏览 13047 次
该帖已经被评为新手帖
作者 正文
   发表时间:2007-04-17  
经典,那可以这样理解了,threadLocal是为一的,里面保存个Map
map里面的key和Thread相关,每一个Thread key唯一。
0 请登录后投票
   发表时间:2007-04-17  
jamesby 写道
经典,那可以这样理解了,threadLocal是为一的,里面保存个Map
map里面的key和Thread相关,每一个Thread key唯一。


这种理解是不正确的。

Thread里面保存了个Map,threadLocal是个map entry key。ThreadLocal的实现决定了这ThreadLocal类的所有的对象,它们的key都不一样,所以可以用来做map entry key。

而同一个ThreadLocal的对象,是可以放在不同的thread的map里面的. 我不熟悉Hibernate,不过看楼主的例子,就是这样的, 这个key可以同时出现在不同的thread的map里面。

这个key所对应的map entry value,是要自己定义的,每次呼叫threadLocalObj.set(myValue),就把thread的里面的map里面的threadLocalObj这个key对应的value更新了。

下面给个捣乱的程序,map entry value是共享的,看看是什么结果。有助于加深理解。


public class TestThreadLocal {
  public static ThreadLocal<Mutable> t1 = new ThreadLocal<Mutable>();
  public static ThreadLocal<Mutable> t2 = new ThreadLocal<Mutable>();
  public static ThreadLocal<Mutable> t3 = new ThreadLocal<Mutable>();
  public static Mutable m = new Mutable();
	
  public static void main(String[] args) {
    TestThread tt1 = new TestThread(t1, 5, 3, "tt1");
    TestThread tt2 = new TestThread(t2, 7, 5, "tt2");
    t3.set(m);
    System.out.println("Main thread: -> " + m.str);

    tt1.start();
    tt2.start();
		
    while(tt1.isAlive() || tt2.isAlive()) {
      Mutable mutable = t3.get();
      try {
        Thread.sleep(2000);
      } catch(Exception ex) { }
      System.out.println("Main thread: -> " + mutable.str);
      System.out.println(m==mutable);
    }
    System.out.println("Final, main thread: -> " + m.str);		
  }
}

class TestThread extends Thread {   
  private ThreadLocal<Mutable> threadLocal;   
  private int count;
  private int sleep;
  private String id;
  
  public TestThread(ThreadLocal<Mutable> threadLocal, int count, int sleep, 
                    String id) {   
    this.threadLocal = threadLocal;         
    this.count = count;
    this.sleep = sleep;
    this.id = id;
  }   
  
  public void run() {
    this.threadLocal.set(TestThreadLocal.m);
  	
    for(int i=1; i<=count; i++) {
      threadLocal.get().str = "Thread " + id + 
  	     " changes mutable, iteration " + i;
      System.out.println(threadLocal.get().str);
      try {
        Thread.sleep(sleep*1000);
      } catch (Exception ex) {};
    }  	
  }
}

class Mutable {
  public String str = "mutable";
}



搂主在第十楼写的程序写错了,第22行该改成

new Thread(new TestThread(testThreadLocal)).start();


你会观察到很有趣的现象。想明白了,这个ThreadLocal就搞明白了。

由于ThreadLocal的编程接口有set, get,很容易让人误会数据是存在这个ThreadLocal对象里面的,实际不是的,数据是存在Thread里面的,ThreadLocal只是提供了操作。

如果Thread的接口改成这样:

Thred.currentThread().getThreadLocalStorage().get(key);
Thred.currentThread().getThreadLocalStorage().put(key, myValue);


就不会引起这么多误会。不过ThreadLocal是Java 1.2才加的,可能是因为不想改变原有Thread的接口。
0 请登录后投票
   发表时间:2007-04-18  
多谢bigpanda的进一步讲解!范例程序暂时还没有看,但是大意已经理解了,就是每个 Thread 都保存一个自己的 Map,key 就是 ThreadLocal 变量,而 value 就是在对应 Thread 运行语句 threadLocal.set(object) 时放进去的那个 object !!所以,即使 ThreadLocal 变量是同一个,但因为在不同的 Thread 里面访问的是不同的 Map,所以 threadLocal.get() 总返回不同的object!!

我现在基本上完全明白了。谢谢bigpanda的时间!

再补充一个小问题,那照这样说,是不是“ThreadLocal 里面有 Map,而当前 Thread 就是key”的说法错误了?
0 请登录后投票
   发表时间:2007-04-18  
非典型程序员 写道
再补充一个小问题,那照这样说,是不是“ThreadLocal 里面有 Map,而当前 Thread 就是key”的说法错误了?


错了,给搞反了。

另外你去看看十楼你写的多线程程序,第22行写错了。
0 请登录后投票
   发表时间:2007-04-18  
bigpanda 写道
非典型程序员 写道
再补充一个小问题,那照这样说,是不是“ThreadLocal 里面有 Map,而当前 Thread 就是key”的说法错误了?


错了,给搞反了。

另外你去看看十楼你写的多线程程序,第22行写错了。


发现了发现了,把 start() 写成 run() 了~~
0 请登录后投票
   发表时间:2007-04-19  
好了,测试代码最后修正版本~~


public class ThreadLocalTest {
    private static final ThreadLocal session = new ThreadLocal();

    private static class TestThread implements Runnable {
        private ThreadLocal sessionInThread;
        public TestThread(ThreadLocal session) {
            sessionInThread = session;
        }
        public void run() {
            sessionInThread.set(new Object());
            showInfo();
            showInfo(); // Show again, for verification.
        }
        private void showInfo() {
            try {
                Thread.sleep(300);
            } catch (InterruptedException ie) {
                return;
            }
            System.out.println(Thread.currentThread().getName()
                    + ":ThreadLocal object passed in:" + sessionInThread
                    + ",ThreadLocal object content: " + sessionInThread.get());
        }
    }
    public static void main(String[] args) {
        for (int i = 0; i < 6; i ++) {
            new Thread(new TestThread(session), "thread_" + i).start();
        }
    }
}

0 请登录后投票
   发表时间:2007-05-25  
public class ThreadLocalTest {   
  
    private static class TestThread implements Runnable {   
        private static final ThreadLocal session = new ThreadLocal();   

        private ThreadLocal sessionInThread;   
        public TestThread() {   
            sessionInThread = session;   
        }   
        public void run() {   
            sessionInThread.set(new Object());   
            showInfo();   
            showInfo(); // Show again, for verification.   
        }   
        private void showInfo() {   
            try {   
                Thread.sleep(300);   
            } catch (InterruptedException ie) {   
                return;   
            }   
            System.out.println(Thread.currentThread().getName()   
                    + " :ThreadLocal object passed in:" + sessionInThread   
                    + ", ThreadLocal object content: " + sessionInThread.get());   
        }   
    }   
    public static void main(String[] args) {   
        for (int i = 0; i < 6; i ++) {   
            new Thread(new TestThread(), "thread_" + i).start();   
        }   
    }   
}   

这样是不更能表达楼主的意思
0 请登录后投票
论坛首页 入门技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics