论坛首页 Java企业应用论坛

ThreadLocal and synchronized 补充

浏览 16625 次
精华帖 (2) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2007-05-25  
shaucle 写道
俺以前一个项目在没有ThreadLocal时的实现...
原理上是差不多的.
0 请登录后投票
   发表时间:2007-05-25  
ThreadLocal
个人觉得最主要是用来线程共享数据!
这个就和request一样!!
0 请登录后投票
   发表时间:2007-05-25  
shaucle 写道
MyThreadLocal{
  Map map = new HashMap();
  get(){
    return map.get(Thread.currentThread());
  }
  set(Object o){
    map.put(Thread.currentThread(), o);
  }
}



dovecat 写道
shaucle 写道
俺以前一个项目在没有ThreadLocal时的实现...
原理上是差不多的.


有点差别。
java的ThreadLocal里面没有自己的map成员,而是把map放到在使用Thread类中。
而shaucle给出的自己threadLocal实现的例子里面拥有一个map,这个map会越来越大,所以还需要有另外的thread来定期清理map中thread已死的那些key
0 请登录后投票
   发表时间:2007-05-25  
恩,我是说原理上,除开后来针对设计上的处理,将ThreadLocal.ThreadLocalMap赋给Thread.threadLocals变量,而ThreadLocal就变成了包装和操作ThreadLocalMap的接口.
0 请登录后投票
   发表时间:2007-05-25  
HashMap的put方法不是同步的,有并发冲突。
threadLocal的map是保存在thread中的,不需要同步。
0 请登录后投票
   发表时间:2007-05-25  
dovecat 写道
http://www.iteye.com/topic/81936?page=1
以上是原贴.
本文只是针对原贴的补充.

对于ThreadLocal和synchronized的区别,请看下面的例子估计大家更能清楚认识.希望我能在kyluan原贴的基础上把这个区别说清楚.

btw:这个例子是一个使用ThreadLocal不当的例子,请不要在项目中如此使用.

public class TestThreadLocal {
  public static void main(String[] args) throws Exception {
    ThreadLocal myThreadLocal = new ThreadLocal();
    StaffInfoVO staff1 = new StaffInfoVO();
    staff1.setName("default");
    staff1.setCount(new Integer(1));
    for (int i = 0; i < 10; i++) {
      MyThread myt = new MyThread(staff1, myThreadLocal);
      Thread t1 = new Thread(myt);
      t1.setName("myThread" + i);
      t1.start();
    }
  }

  public static class MyThread implements Runnable {

    private StaffInfoVO staff1;

    private ThreadLocal myThreadLocal;

    public MyThread(StaffInfoVO staff, ThreadLocal myThreadLocal) {
      staff1 = staff;
      this.myThreadLocal = myThreadLocal;
      System.out.println("con staff" + staff);
    }

    public void run() {
      myThreadLocal.set(staff1);
      while (true) {
        StaffInfoVO staff = (StaffInfoVO) myThreadLocal.get();
        // System.out.println("staff:" + staff);
        int i = staff.getCount().intValue();
        System.out.println("Thread name:" + Thread.currentThread().getName() + " staff count:" + i);

        staff.setCount(new Integer(i + 1));

        try {
          Thread.currentThread().sleep(1000);
        } catch (InterruptedException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
      }
    }
  }
}
public class StaffInfoVO implements Serializable {

  /**
   * 
   */
  private static final long serialVersionUID = -57676961756664705L;

  private String name;

  private String staffNo;

  private List roles;

  private Integer count;

  // 如果不使用synchronized,大家一眼就看到区别了.
  public synchronized Integer getCount() {
    return count;
  }
  
  // 如果不使用synchronized,大家一眼就看到区别了.
  public synchronized void setCount(Integer count) {
    this.count = count;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public List getRoles() {
    return roles;
  }

  public void setRoles(List roles) {
    this.roles = roles;
  }

  public String getStaffNo() {
    return staffNo;
  }

  public void setStaffNo(String staffNo) {
    this.staffNo = staffNo;
  }

}


首先产生一个引用对象staff1 .
然后产生10个MyThread 类型的线程对象.
这10个线程中每一个都持有私有的ThreadLocal对象.
在这10个线程开始start的时候,在run方法中将staff1对象设置到各自私有的ThreadLocal对象中.

当我们注释掉staff1中的synchronized时候,我们马上就看到出现了count数据的不一致.
只有当我们使用synchronized,不一致的情况解决了.
当StaffInfoVO的count的set/get方法不是synchronized的时候,虽然每个Thread维护了一个对应的ThreadLocal,而ThreadLocal只是对于Thread对象中的ThreadLocalMap的接口暴露(get/set添加和取得保存的对象)对于被保存的对象,只是保存了引用的情况下,并且被保存对象的状态改变的方法不是synchronized的时候,并不能保证同步. 这个是ThreadLocal的局限性.并没有synchronized关键字那么安全.在我看来ThreadLocal MS不是用来处理同步,而只是为了保存当前线程自有的某个状态.当ThreadLocal.set(Object obj)方法时,取得Thread.currentThread.ThreadLocalMap对象,然后将自己作为KEY保存到ThreadLocalMap中.ThreadLocal.get处理方法类似.

所以最后我得出的结论就是:synchronized是用来处理多线程环境下的数据同步,而ThreadLocal只是为了保存当前线程私有的某种状态.

以上内容,如有不当,请指出.
谢谢.


首先我声明:你没有看完原贴的全文就妄加评论!!!
1。例子只是为了说明ThreadLocal,并不是叫你那样去写代码!
2.原文的目的是说明在多线程程序中什么时候用synchronized ,什么时候用ThreadLocal.
并不打算去深入的说明ThreadLocal.
3.原文已经总结了"但是ThreadLocal与synchronized有本质的区别。synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。
Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。
当然ThreadLocal并不能替代synchronized,它们处理不同的问题域。Synchronized用于实现同步机制,比ThreadLocal更加复杂"
很多的朋友都没有看完全文说胡乱评论!!!

0 请登录后投票
   发表时间:2007-05-25  
klyuan 写道

首先我声明:你没有看完原贴的全文就妄加评论!!!
1。例子只是为了说明ThreadLocal,并不是叫你那样去写代码!
2.原文的目的是说明在多线程程序中什么时候用synchronized ,什么时候用ThreadLocal.
并不打算去深入的说明ThreadLocal.
3.原文已经总结了"但是ThreadLocal与synchronized有本质的区别。synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。
Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。
当然ThreadLocal并不能替代synchronized,它们处理不同的问题域。Synchronized用于实现同步机制,比ThreadLocal更加复杂"
很多的朋友都没有看完全文说胡乱评论!!!


我并不是评论你的贴子好不好什么的.
只是深入一下,将一些感觉上模拟两可的东西用一个例子表述清楚下.
在这个例子中不是一眼就看出什么时候该用ThreadLocal什么时候用synchronized了吗?
最后研究一下ThreadLocal的代码吧:
ThreadLocal代码片段:
public Object get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) 
            return map.get(this);

        // Maps are constructed lazily.  if the map for this thread
        // doesn't exist, create it, with this ThreadLocal and its
        // initial value as its only entry.
        Object value = initialValue();
        createMap(t, value);
        return value;
    }
public void set(Object value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) 
            map.set(this, value);
        else
            createMap(t, value);
    }
ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
void createMap(Thread t, Object firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

以上代码就看出,取的是当前线程持有的ThreadLocalMap,然后从这个Map中取对象.
ThreadLocalMap的代码片段:
static class ThreadLocalMap {

        /**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */ 
        private static class Entry extends WeakReference {
            /** The value associated with this ThreadLocal. */
            private Object value;

            private Entry(ThreadLocal k, Object v) {
                super(k);
                value = v;
            }
        }

ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }
// 被ThreadLocal的get/set方法暴露给外部调用
private Object get(ThreadLocal key) {
            // 通过ThreadLocal的hashcode和entry[] table 的大小获得index
            int i = key.threadLocalHashCode & (table.length - 1);
            Entry e = table[i];
            if (e != null && e.get() == key)
                return e.value;
            // 注意在此方法中将调用ThreadLocal.initialValue方法.
            return getAfterMiss(key, i, e);
        }
private void set(ThreadLocal key, Object value) {

            // We don't use a fast path as with get() because it is at 
            // least as common to use set() to create new entries as 
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);

            for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {
                Object k = e.get();

                if (k == key) {
                    e.value = value;
                    return;
                }

                if (k == null) {
                    replaceStaleEntry(key, value, i, false);
                    return;
                }
            }
            // 重新将ThreadLocal的size table 和threshold调整大小,便于重新计算hashcode
            tab[i] = new Entry(key, value);
            if (++size >= threshold) 
                rehash();
        }


Thread代码片段:
    ThreadLocal.ThreadLocalMap threadLocals = null;


有兴趣的可以自己去研究下.
0 请登录后投票
   发表时间:2007-05-25  
   作为新手我赞同lz的做法,并不是每个人都对每个lz的看法和见解都能很好或是100%的理解,我没有看过原贴,但是我从这张贴上学到了不少,当然包括原贴作者的宝贵见解,每个人在理解别人的东西时候加上自己的见解和猜测时本来就是一种学习的过程,难道在别人思考你的见解时你不该高兴点吗?作为比别人强的人,让别人的思维转向一个更正确的方向不是更让人尊敬些麻!也许是我水平问题,比较喜欢这种jdk的基础讨论,感觉通过这些成长不少,所以不管是好的还是坏的,我都希望从这些中得到更多的启发。作为一个成长的新手我也会更尊敬各位走在我老前面的老大!  :)
0 请登录后投票
   发表时间:2007-05-25  
Qieqie 写道
shaucle 写道
MyThreadLocal{
  Map map = new HashMap();
  get(){
    return map.get(Thread.currentThread());
  }
  set(Object o){
    map.put(Thread.currentThread(), o);
  }
}



dovecat 写道
shaucle 写道
俺以前一个项目在没有ThreadLocal时的实现...
原理上是差不多的.


有点差别。
java的ThreadLocal里面没有自己的map成员,而是把map放到在使用Thread类中。
而shaucle给出的自己threadLocal实现的例子里面拥有一个map,这个map会越来越大,所以还需要有另外的thread来定期清理map中thread已死的那些key
\

俺只是随意写一下,说明一下一些context的一般实现原理
不过程序员一般都喜欢detail 进去...
0 请登录后投票
   发表时间:2007-05-25  
klyuan 写道

3.原文已经总结了"但是ThreadLocal与synchronized有本质的区别。synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。
Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。
当然ThreadLocal并不能替代synchronized,它们处理不同的问题域。Synchronized用于实现同步机制,比ThreadLocal更加复杂"
很多的朋友都没有看完全文说胡乱评论!!!

synchronized关键字的目的并不是提供线程间的数据共享,而是为多线程共享的数据结构提供一个互斥机制,保护共享数据处在一个一致的状态,避免在多线程访问时出现race condition破坏共享数据的一致性。
我所知道的并发编程模型有两种:基于共享内存和基于消息传递。Java属于共享内存的并发模型,而erlang这样的属于消息传递模型。换句话来说,在线程间共享数据,如果不在意数据一致性的话,直接共享就可以了。所以synchronized用于在线程间共享数据的这个说法其实是不对的。

ThreadLocal前面有人说的很清楚了,其实就是thread private data, 前面几位有自己实现thread local的,其实都不能算错。effective java里的Item 32有一节就在讨论ThreadLocal for capabilities. 算是标准答案吧。当然了,往ThreadLocal里面塞一个要给多个线程共享的数据也没什么不妥的,但是这个用法是如此诡异,几乎要划到反模式里面去了吧?
0 请登录后投票
论坛首页 Java企业应用版

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