论坛首页 Java企业应用论坛

ThreadLocal示例

浏览 16641 次
精华帖 (2) :: 良好帖 (13) :: 新手帖 (13) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-09-09   最后修改:2010-09-09
其实楼主那个例子对于多个线程访问来说,是没办法实现共享同一个实例,在ThreadLocalMap里面,是以线程为Key,也就是说多线程时,会实例化多个SimpleDateFormat。如果真的需要共享同一个实例,我觉得可以用单例模式来实现
0 请登录后投票
   发表时间:2010-09-09  
zhang34082 写道
其实楼主那个例子对于多个线程访问来说,是没办法实现共享同一个实例,在ThreadLocalMap里面,是以线程为Key,也就是说多线程时,会实例化多个SimpleDateFormat。如果真的需要共享同一个实例,我觉得可以用单例模式来实现

单例模式必须同步,因为SimpleDateFormat不是线程安全的,有性能瓶颈

另外,楼主的例子有个问题,就是,SimpleDateFormat的pattern必须是唯一的,实际项目中可能需要各种各样的pattern,怎么处理这个问题?
0 请登录后投票
   发表时间:2010-09-09  
貌似有点像单例模式中的懒加载................
0 请登录后投票
   发表时间:2010-09-09   最后修改:2010-09-09
zhang34082 写道
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class DateUtil {

  private static final String DATE_FORMAT = "yyyy-MM-dd";

  private static Map<String, DateFormat> map = new HashMap<String, DateFormat>();
  
  private static String lock = "lock";
  
  public static DateFormat getInstance(String pattern) {
    DateFormat instance = map.get(pattern);
    if (instance == null) { 
      synchronized(lock) {            // 线程同步,只让一个线程进来实例化
        instance = map.get(pattern);  // 如果两个线程同时取到的都为null时,其中一个线程实例化后,另一个线程就不需要实例化了
        if (instance == null) { 
          instance = new SimpleDateFormat(DATE_FORMAT);
          map.put(pattern, instance);
        }
      }
    }
    
    return instance;
  }
  
  public static DateFormat getDateFormat(String pattern) {
    return getInstance(pattern);
  }

  public static Date parse(String textDate, String pattern) throws ParseException {
    if (pattern == null || pattern.equals("")) {
      pattern = DATE_FORMAT;
    }
    if (textDate == null || textDate.equals("")) {
      return null;
    }
    return getDateFormat(pattern).parse(textDate);
  }

}


对于各种不同的pattern,又保持多线程下同一个pattern共享同一个实例,以上代码可以实现,不妥之处,欢迎指正。



是不是应该这样子呀
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class DateUtil {

  //private static final String DATE_FORMAT = "yyyy-MM-dd";

  private static Map<String, DateFormat> map = new HashMap<String, DateFormat>();
 
  private static String lock = "lock";
 
  public static DateFormat getInstance(String pattern) {
    DateFormat instance = map.get(pattern);
    if (instance == null) {
      synchronized(lock) {            // 线程同步,只让一个线程进来实例化
        instance = map.get(pattern);  // 如果两个线程同时取到的都为null时,其中一个线程实例化后,另一个线程就不需要实例化了
        if (instance == null) {
          instance = new SimpleDateFormat(pattern); //pattern 类似 yyyy-MM-dd 的date format
          map.put(pattern, instance);
        }
      }
    }
   
    return instance;
  }
 
  public static DateFormat getDateFormat(String pattern) {
    return getInstance(pattern);
  }

  public static Date parse(String textDate, String pattern) throws ParseException {
   
    if (textDate == null || textDate.equals("")) {
      return null;
    }
    return getDateFormat(pattern).parse(textDate);
  }

}
而且 hashmap本身就是非线程安全的 这个在多线程环境下还是有问题吧
0 请登录后投票
   发表时间:2010-09-09  
分离的北极熊 写道
ThreadLocal 以空间换时间
Synchornized 以时间换空间

ThreadLocal 同一个线程下确保获取的对象是唯一的,对其修改不会影响到其他线程。
其中initialValue()是一个空方法

疑问:
private static ThreadLocal<SimpleDateFormat> threadlocal = new ThreadLocal<SimpleDateFormat>(){
  protected synchronized Object initialValue(){
      reutrn new SimpleDateFormat(format);
  }
};
每个ThreadLocal在多线程下也能确保拿到的对象和其他线程不相同
而上面也提到了ThreadLocal是空间换时间,但是里面覆盖的initialValue() 却又加上了synchornized字段,这是不是有点相互矛盾?

疑问的地方在这,望解答。




加这个synchornized 就是由于SimpleDateFormat 是非线程安全 所以要同步
例如 线程a threadlocal.get()方法是这样执行 先获取当前的thread a 然后通过thread a来获取当前线程的 ThreadLocalMap(每个线程都有各自的ThreadLocalMap) 然后以threadlocal为key来get value 由于第一次获取的ThreadLocalMap 是null 此时会调用initialValue方法来new SimpleDateFormat(format) 。注意此时需要同步控制 (因为可能线程b此时也可能做new SimpleDateFormat(format)这个动作 线程a与线程b是同一个同步锁threadlocal )
注意各个线程用的key都是同一个threadlocal (threadlocal 是static 各个线程是能共享访问的)

其实这样只是保证了在线程a 的整个执行过程中 能获取到同一个SimpleDateFormat对象实例 如果有1000个线程 还是创建了1000个 SimpleDateFormat对象实例 
总结使用threadlocal只是为了避免了将这个对象作为参数传递的麻烦
0 请登录后投票
   发表时间:2010-09-09  
pengjunwu 写道


hashmap本身就是非线程安全的 这个在多线程环境下还是有问题吧

你改后的代码与我的没什么区别,hashMap虽然是非线性安全的,但是在取instance时,通过synchronized保证只有一个线程进入,并实例化。
附上测试代码:

public class DateUtilTest implements Runnable {
  
  public void run() {
    try {
      System.out.println(DateUtil.parse("2008-01-04", null));  
    } catch (Exception e) {
      e.printStackTrace();
    }
    
  }

  public static void main(String[] args) {
    
    for (int i = 0; i < 5; i++) {
      Thread t = new Thread(new DateUtilTest());
      t.start();
    }
  }
}

0 请登录后投票
   发表时间:2010-09-09  
zhang34082 写道
pengjunwu 写道


hashmap本身就是非线程安全的 这个在多线程环境下还是有问题吧

你改后的代码与我的没什么区别,hashMap虽然是非线性安全的,但是在取instance时,通过synchronized保证只有一个线程进入,并实例化。
附上测试代码:

public class DateUtilTest implements Runnable {
  
  public void run() {
    try {
      System.out.println(DateUtil.parse("2008-01-04", null));  
    } catch (Exception e) {
      e.printStackTrace();
    }
    
  }

  public static void main(String[] args) {
    
    for (int i = 0; i < 5; i++) {
      Thread t = new Thread(new DateUtilTest());
      t.start();
    }
  }
}


兄弟 我的意思是这句
private static Map<String, DateFormat> map = new HashMap<String, DateFormat>();
map这个共享对象是非线程安全的 如果你在多线程操作它时会有问题的吧(如set操作)就象你定义一个全局共享的SimpleDateFormat对象一样是非线程安全的啦
private static SimpleDateFormat format= new SimpleDateFormat(“yyyy”);
0 请登录后投票
   发表时间:2010-09-10  
pouyang 写道
引用
每个线程第一次获取都会初始化一次,你的实践验证能力很好,可能我开始描述的不是很清晰;
每一个线程初始化一次,每一个线程自身维护一个map,空间上占用内存较多,但不需要同步节约了时间;

那把那个SimpleDateFormat对象的 static 修饰符去掉多好,
写成这样SimpleDateFormat s = new SimpleDateFormat(DATE_FORMAT);  就不需要考虑同步了,而且还不用去new ThreadLocal() ,
我的理解是用了ThreadLocal就可以跨类跨方法调用这个SimpleDateFormat 了(方法不用带参数)(robbin说的)
我是不是还没开窍啊。


我自己大概想明白了一些,如果在一个线程里面要调
public static DateFormat getDateFormat() {   
        return (DateFormat) threadLocal.get();   
 } 
这个方法,调20次的话,
写成SimpleDateFormat s = new SimpleDateFormat(DATE_FORMAT);  这样要创建很多对象,而且这是个工具类,恕我没看清楚
弄成static SimpleDateFormat s = new SimpleDateFormat(DATE_FORMAT);  并发会出问题

这个时候用了ThreadLocal的话,那么只需要在这个线程里实例化一次就ok了,而且多并发也不会出问题。
0 请登录后投票
   发表时间:2010-09-10  
实在是以空间换了时间,解惑了,谢谢了
我现在积分只有20分,30分才可以投票,要不给你来个精华了
0 请登录后投票
   发表时间:2010-09-12  
pouyang 写道
实在是以空间换了时间,解惑了,谢谢了
我现在积分只有20分,30分才可以投票,要不给你来个精华了

分享下自己的认识,主要在于交流,共同提高;
0 请登录后投票
论坛首页 Java企业应用版

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