锁定老帖子 主题:ThreadLocal示例
精华帖 (2) :: 良好帖 (13) :: 新手帖 (13) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2010-09-09
最后修改:2010-09-09
其实楼主那个例子对于多个线程访问来说,是没办法实现共享同一个实例,在ThreadLocalMap里面,是以线程为Key,也就是说多线程时,会实例化多个SimpleDateFormat。如果真的需要共享同一个实例,我觉得可以用单例模式来实现
|
|
返回顶楼 | |
发表时间:2010-09-09
zhang34082 写道 其实楼主那个例子对于多个线程访问来说,是没办法实现共享同一个实例,在ThreadLocalMap里面,是以线程为Key,也就是说多线程时,会实例化多个SimpleDateFormat。如果真的需要共享同一个实例,我觉得可以用单例模式来实现
单例模式必须同步,因为SimpleDateFormat不是线程安全的,有性能瓶颈 另外,楼主的例子有个问题,就是,SimpleDateFormat的pattern必须是唯一的,实际项目中可能需要各种各样的pattern,怎么处理这个问题? |
|
返回顶楼 | |
发表时间:2010-09-09
貌似有点像单例模式中的懒加载................
|
|
返回顶楼 | |
发表时间: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本身就是非线程安全的 这个在多线程环境下还是有问题吧 |
|
返回顶楼 | |
发表时间: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只是为了避免了将这个对象作为参数传递的麻烦 |
|
返回顶楼 | |
发表时间: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(); } } } |
|
返回顶楼 | |
发表时间: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”); |
|
返回顶楼 | |
发表时间: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了,而且多并发也不会出问题。 |
|
返回顶楼 | |
发表时间:2010-09-10
实在是以空间换了时间,解惑了,谢谢了
我现在积分只有20分,30分才可以投票,要不给你来个精华了 |
|
返回顶楼 | |
发表时间:2010-09-12
pouyang 写道 实在是以空间换了时间,解惑了,谢谢了
我现在积分只有20分,30分才可以投票,要不给你来个精华了 分享下自己的认识,主要在于交流,共同提高; |
|
返回顶楼 | |