`
dovecat
  • 浏览: 39389 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

ThreadLocal and synchronized 补充

    博客分类:
  • java
阅读更多
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只是为了保存当前线程私有的某种状态.

以上内容,如有不当,请指出.
谢谢.
分享到:
评论
31 楼 nepalon 2008-04-14  
LZ写的在getCount和setCount中用synchronized也不能达到同步效果的,要改为
synchronized (staff) {
        int i = staff.getCount().intValue();  
        System.out.println("Thread name:" + Thread.currentThread().getName() + " staff count:" + i);  
 
        staff.setCount(new Integer(i + 1));  

}
30 楼 ecoll 2007-06-16  
两者面向的问题领域不同。同步机制是为了同步多个线程对相同资源的并发访问,是为了多个线程之间进行通信的有效方式;而ThreadLocal是隔离多个线程的数据共享,从根本上就不在多个线程之间共享资源(变量),这样当然不需要对多个线程进行同步了。所以,如果你需要进行多个线程之间进行通信,则使用同步机制;如果需要隔离多个线程之间的共享冲突,可以使用ThreadLocal
29 楼 max.h.chen 2007-05-27  
klyuan 写道
保护数据的一致性ThreadLocal会更好用一点!
消息传递,也就是我说的“线程间共享数据”synchronized


开什么玩笑呢,消息传递和共享内存是两种线/进程间数据共享的方式,这前面已经有人说过了。怎么就得出‘消息传递,也就是我说的“线程间共享数据”synchronized’这样的结论呢?消息传递是将大家需要共享的数据以消息的方式,通过定义和实现消息栈,来达到数据的共享的目的。而共享内存则是将数据写到一个大家都能互访问的同一块内存区域(映射为各自的虚拟内存地址),为了防止race condition所以才有synchronized机制。synchronized是共享内存搞出来的玩意,跟数据共享没什么关系,这是个白马非马的问题。概念就没搞清楚。
28 楼 shaucle 2007-05-25  
你可以看下源码,并不是你们说的那么复杂

都是人想出来的解决方案嘛.

27 楼 shaucle 2007-05-25  
jindw 写道
shaucle 写道
MyThreadLocal{
  Map map = new HashMap();
  get(){
    return map.get(Thread.currentThread());
  }
  set(Object o){
    map.put(Thread.currentThread(), o);
  }
}


这样更加是误导,ThreadLocal是与Thread紧密耦合的,没有上面这么简单。


但这的确是我以前一个项目的实现
没有ThreadLocal之前Thread和ThreadLocal无法紧密耦合(你不能改Thead)
紧密耦合主要是实现线程之间传递inheritableThreadLocals的问题

ThreadLocalMap是ThreadLocal内部的一个WeakReference的Hashmap实现
而Thread.currentThread();已经处理了线程问题,所以synchronized也可以不用

至于是否误导,我看不同的人应该有不同的看法。
ps:本来我不想很detail的。
26 楼 jindw 2007-05-25  
shaucle 写道
MyThreadLocal{
  Map map = new HashMap();
  get(){
    return map.get(Thread.currentThread());
  }
  set(Object o){
    map.put(Thread.currentThread(), o);
  }
}


这样更加是误导,ThreadLocal是与Thread紧密耦合的,没有上面这么简单。
25 楼 jindw 2007-05-25  
dovecat 写道
shaucle 写道
ThreadLocal理解成CurrentThreadContext就行了.
也对!其实我们光看这个类的名字就知道了.ThreadLocal!
非常言简意赅.也许叫ThreadLocalDataHolder更好.^_^


我觉得应该叫ThreadLocalDataRef
因为,ThreadLocal本身是不持有对象的。就算ThreadLocal没被销毁,与他相关的对象也可能被回收
24 楼 shaucle 2007-05-25  
越来越偏...
23 楼 Qieqie 2007-05-25  
“数据一致性”和ThreadLocal无关,这是两个概念
klyuan似乎混淆了概念。
所谓的数据一致性,是指数据从一个状态到另外一个状态要保持一致性,不能被破坏。
一般在并发对共享数据的访问不加有效控制就可能导致不一致性。

你说的通过java程序加同步锁来保证按顺序访问共享数据保持一致性当然没有问题。
但是ThreadLocal与此没有关系,很多人已经指出这一点了:

ThreadLocal里面保存的是每个线程独有的数据,这些数据不是并发“共享数据”,对这些数据没有“一致性”的说法

22 楼 BirdGu 2007-05-25  
引用
保护数据的一致性ThreadLocal会更好用一点!
消息传递,也就是我说的“线程间共享数据”synchronized


ThreadLocal都没有共享数据了,还有什么一致性问题?原来你解决问题的方法就是取消这个问题?
21 楼 klyuan 2007-05-25  
xin_wang 写道
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里面塞一个要给多个线程共享的数据也没什么不妥的,但是这个用法是如此诡异,几乎要划到反模式里面去了吧?


保护数据的一致性ThreadLocal会更好用一点!
消息传递,也就是我说的“线程间共享数据”synchronized
20 楼 BirdGu 2007-05-25  
引用
当然了,往ThreadLocal里面塞一个要给多个线程共享的数据也没什么不妥的,但是这个用法是如此诡异,几乎要划到反模式里面去了吧?


即使要这么做(想不出来这样做的理由),为了保证这个数据的一致性,synchronized仍然是需要的。
19 楼 xin_wang 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里面塞一个要给多个线程共享的数据也没什么不妥的,但是这个用法是如此诡异,几乎要划到反模式里面去了吧?
18 楼 shaucle 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 进去...
17 楼 moshalanye 2007-05-25  
   作为新手我赞同lz的做法,并不是每个人都对每个lz的看法和见解都能很好或是100%的理解,我没有看过原贴,但是我从这张贴上学到了不少,当然包括原贴作者的宝贵见解,每个人在理解别人的东西时候加上自己的见解和猜测时本来就是一种学习的过程,难道在别人思考你的见解时你不该高兴点吗?作为比别人强的人,让别人的思维转向一个更正确的方向不是更让人尊敬些麻!也许是我水平问题,比较喜欢这种jdk的基础讨论,感觉通过这些成长不少,所以不管是好的还是坏的,我都希望从这些中得到更多的启发。作为一个成长的新手我也会更尊敬各位走在我老前面的老大!  :)
16 楼 dovecat 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;


有兴趣的可以自己去研究下.
15 楼 klyuan 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更加复杂"
很多的朋友都没有看完全文说胡乱评论!!!

14 楼 Godlikeme 2007-05-25  
HashMap的put方法不是同步的,有并发冲突。
threadLocal的map是保存在thread中的,不需要同步。
13 楼 dovecat 2007-05-25  
恩,我是说原理上,除开后来针对设计上的处理,将ThreadLocal.ThreadLocalMap赋给Thread.threadLocals变量,而ThreadLocal就变成了包装和操作ThreadLocalMap的接口.
12 楼 Qieqie 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

相关推荐

Global site tag (gtag.js) - Google Analytics