- 浏览: 288845 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
jj19900703:
多个pattern代码如下:<pre name=&quo ...
ThreadLocal示例 -
jj19900703:
上面这些评论真是搞笑,根本没弄懂瞎评论误导人。第一:Simpl ...
ThreadLocal示例 -
myumen:
<div class="quote_title ...
ThreadLocal示例 -
helong0904:
<div class="quote_title ...
ThreadLocal示例 -
myumen:
<div class="quote_title ...
ThreadLocal示例
本文借花献佛,引用Tim Cull的博文“SimpleDateFormat: Performance Pig”介绍下ThreadLocal的简单使用,同时也对SimpleDateFormat的使用有个深入的了解。
Tim Cull 写道
Just yesterday I came across this problem “in the wild” for the third time in my career so far: an application with performance problems creating tons of java.text.SimpleDateFormat instances. So, I have to get this out there: creating a new instance of SimpleDateFormat is incredibly expensive and should be minimized. In the case that prompted this post, I was using JProfiler to profile this code that parses a CSV file and discovered that 50% of the time it took to suck in the file and make 55,000 objects out of it was spent solely in the constructor of SimpleDateFormat. It created and then threw away a new one every time it had to parse a date. Whew!
“Great,” you think, “I’ll just create one, static instance, slap it in a field in a DateUtils helper class and life will be good.”
Well, more precisely, life will be good about 97% of the time. A few days after you roll that code into production you’ll discover the second cool fact that’s good to know: SimpleDateFormat is not thread safe. Your code will work just fine most of the time and all of your regression tests will probably pass, but once your system gets under a production load you’ll see the occasional exception.
“Fine,” you think, “I’ll just slap a ’synchronized’ around my use of that one, static instance.”
Ok, fine, you could do that and you’d be more or less ok, but the problem is that you’ve now taken a very common operation (date formatting and parsing) and crammed all of your otherwise-lovely, super-parallel application through a single pipe to get it done.
“Great,” you think, “I’ll just create one, static instance, slap it in a field in a DateUtils helper class and life will be good.”
Well, more precisely, life will be good about 97% of the time. A few days after you roll that code into production you’ll discover the second cool fact that’s good to know: SimpleDateFormat is not thread safe. Your code will work just fine most of the time and all of your regression tests will probably pass, but once your system gets under a production load you’ll see the occasional exception.
“Fine,” you think, “I’ll just slap a ’synchronized’ around my use of that one, static instance.”
Ok, fine, you could do that and you’d be more or less ok, but the problem is that you’ve now taken a very common operation (date formatting and parsing) and crammed all of your otherwise-lovely, super-parallel application through a single pipe to get it done.
大致意思:Tim Cull碰到一个SimpleDateFormat带来的严重的性能问题,该问题主要有SimpleDateFormat引发,创建一个SimpleDateFormat实例的开销比较昂贵,解析字符串时间时频繁创建生命周期短暂的实例导致性能低下。即使将SimpleDateFormat定义为静态类变量,貌似能解决这个问题,但是SimpleDateFormat是非线程安全的,同样存在问题,如果用‘synchronized’线程同步同样面临问题,同步导致性能下降(线程之间序列化的获取SimpleDateFormat实例)。
Tim Cull使用Threadlocal解决了此问题,对于每个线程SimpleDateFormat不存在影响他们之间协作的状态,为每个线程创建一个SimpleDateFormat变量的拷贝或者叫做副本,代码如下:
import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; /** * 使用ThreadLocal以空间换时间解决SimpleDateFormat线程安全问题。 * @author * */ public class DateUtil { private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; @SuppressWarnings("rawtypes") private static ThreadLocal threadLocal = new ThreadLocal() { protected synchronized Object initialValue() { return new SimpleDateFormat(DATE_FORMAT); } }; public static DateFormat getDateFormat() { return (DateFormat) threadLocal.get(); } public static Date parse(String textDate) throws ParseException { return getDateFormat().parse(textDate); } }
创建一个ThreadLocal类变量,这里创建时用了一个匿名类,覆盖了initialValue方法,主要作用是创建时初始化实例。也可以采用下面方式创建;
//第一次调用get将返回null private static ThreadLocal threadLocal = new ThreadLocal(); //获取线程的变量副本,如果不覆盖initialValue,第一次get返回null,故需要初始化一个SimpleDateFormat,并set到threadLocal中 public static DateFormat getDateFormat() { DateFormat df = (DateFormat) threadLocal.get(); if(df==null){ df = new SimpleDateFormat(DATE_FORMAT) threadLocal.set(df); } return df; }
我们看下我们覆盖的initialValue方法:
protected T initialValue() { return null;//直接返回null }
评论
50 楼
jj19900703
2017-08-08
多个pattern代码如下:
public class DateUtil { private static final ThreadLocal<DateFormat> normalFormat = new ThreadLocal<DateFormat>(); private static final ThreadLocal<DateFormat> simpleFormat = new ThreadLocal<DateFormat>(); private static final ThreadLocal<DateFormat> simpleLongFormat = new ThreadLocal<DateFormat>(); private static final String NORMAL_FORMAT = "yyyy-MM-dd HH:mm:ss"; private static final String SIMPLE_FORMAT = "yyyyMMddHHmmss"; private static final String SIMPLE_LONG__FORMAT = "yyyyMMddHHmmssSSSS"; public static enum DatePattern { NORMAL_FORMAT, SIMPLE_FORMAT, SIMPLE_LONG__FORMAT } private static final DateFormat getNormalFormat() { DateFormat df = normalFormat.get(); if (df == null) { df = new SimpleDateFormat(NORMAL_FORMAT, Locale.getDefault()); normalFormat.set(df); } return df; } private static final DateFormat getSimpleDateFormat() { DateFormat df = simpleFormat.get(); if (df == null) { df = new SimpleDateFormat(SIMPLE_FORMAT, Locale.getDefault()); simpleFormat.set(df); } return df; } private static final DateFormat getSimpleLongDateFormat() { DateFormat df = simpleLongFormat.get(); if (df == null) { df = new SimpleDateFormat(SIMPLE_LONG__FORMAT, Locale.getDefault()); simpleLongFormat.set(df); } return df; } public static Date parse(String source, DatePattern pattern) throws ParseException { switch (pattern) { case NORMAL_FORMAT: return getNormalFormat().parse(source); case SIMPLE_FORMAT: return getSimpleDateFormat().parse(source); case SIMPLE_LONG__FORMAT: return getSimpleLongDateFormat().parse(source); default: return getNormalFormat().parse(source); } } public static String format(Date date, DatePattern pattern) { switch (pattern) { case NORMAL_FORMAT: return getNormalFormat().format(date); case SIMPLE_FORMAT: return getSimpleDateFormat().format(date); case SIMPLE_LONG__FORMAT: return getSimpleLongDateFormat().format(date); default: return getNormalFormat().format(date); } } }
49 楼
jj19900703
2017-08-07
上面这些评论真是搞笑,根本没弄懂瞎评论误导人。
第一:SimpleDateFormat对象压根不能多个线程同时使用,因为它是有状态的,format和parse方法都不能在多线程情况下安全调用。所以一个线程一个SimpleDateFormat是最理想状态
第二:举个实际例子。javaweb开发。一个访问线程进来后,你可能需要在控制层创建一个SimpleDateFormat,然后逻辑层也要用到这个,那么再创建一个,然后持久层也要用到,那么继续创建一个。所以导致了很大的开销。这个时候用ThreadLocal就能解决同一个线程,不同层次之间的数据共享问题。
第一:SimpleDateFormat对象压根不能多个线程同时使用,因为它是有状态的,format和parse方法都不能在多线程情况下安全调用。所以一个线程一个SimpleDateFormat是最理想状态
第二:举个实际例子。javaweb开发。一个访问线程进来后,你可能需要在控制层创建一个SimpleDateFormat,然后逻辑层也要用到这个,那么再创建一个,然后持久层也要用到,那么继续创建一个。所以导致了很大的开销。这个时候用ThreadLocal就能解决同一个线程,不同层次之间的数据共享问题。
48 楼
myumen
2015-02-06
helong0904 写道
在世界的中心呼喚愛 写道
helong0904 写道
zhang34082 写道
……
这个案例解决了一个系统中正真减少创建SimpleDateFormat对象的开销。好思想好思路
换到缓存也可以实现。
这个代码还是有问题,根本原因在于SimpleDateFormat对象不是线程安全的,所以不能在线程之间共享,除非加上同步机制。正确的思路应该是每个线程下,每个pattern一个SimpleDateFormat对象,这样子就解决问题了。
这里面不是已经加了同步,不过感觉如果加了同步效率不是很理想了
你的同步只是创建SimpleDateFormat对象的时候同步了,是为了多个线程使用同一个SimpleDateFormat实例。但实际上SimpleDateFormat对象压根不能多个线程同时使用,因为它是有状态的,format和parse方法都不能在多线程情况下安全调用,只是平时一般遇不到问题而已。
你试试开5个下面的线程同时运行。
class DateFormatThread extends Thread { /** 多线程同时访问会有问题,因为不是线程安全的 */ private static final SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss"); private static boolean runing = true; @Override public void run() { while (runing) { try { this.join(2000); } catch (InterruptedException e1) { e1.printStackTrace(); } try { System.out.println(this.getName() + ":" + sdf.parse("2013-05-24 06:02:20")); } catch (Exception e) { e.printStackTrace(); runing = false; } } } }
47 楼
helong0904
2015-01-30
在世界的中心呼喚愛 写道
helong0904 写道
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共享同一个实例,以上代码可以实现,不妥之处,欢迎指正。
这个案例解决了一个系统中正真减少创建SimpleDateFormat对象的开销。好思想好思路
换到缓存也可以实现。
在世界的中心呼喚愛 写道
helong0904 写道
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共享同一个实例,以上代码可以实现,不妥之处,欢迎指正。
这个案例解决了一个系统中正真减少创建SimpleDateFormat对象的开销。好思想好思路
换到缓存也可以实现。
myumen 写道
在世界的中心呼喚愛 写道
helong0904 写道
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共享同一个实例,以上代码可以实现,不妥之处,欢迎指正。
这个案例解决了一个系统中正真减少创建SimpleDateFormat对象的开销。好思想好思路
换到缓存也可以实现。
这个代码还是有问题,根本原因在于SimpleDateFormat对象不是线程安全的,所以不能在线程之间共享,除非加上同步机制。正确的思路应该是每个线程下,每个pattern一个SimpleDateFormat对象,这样子就解决问题了。
这里面不是已经加了同步,不过感觉如果加了同步效率不是很理想了
46 楼
myumen
2015-01-05
在世界的中心呼喚愛 写道
helong0904 写道
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共享同一个实例,以上代码可以实现,不妥之处,欢迎指正。
这个案例解决了一个系统中正真减少创建SimpleDateFormat对象的开销。好思想好思路
换到缓存也可以实现。
这个代码还是有问题,根本原因在于SimpleDateFormat对象不是线程安全的,所以不能在线程之间共享,除非加上同步机制。正确的思路应该是每个线程下,每个pattern一个SimpleDateFormat对象,这样子就解决问题了。
45 楼
在世界的中心呼喚愛
2014-11-23
helong0904 写道
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共享同一个实例,以上代码可以实现,不妥之处,欢迎指正。
这个案例解决了一个系统中正真减少创建SimpleDateFormat对象的开销。好思想好思路
换到缓存也可以实现。
44 楼
helong0904
2014-06-20
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共享同一个实例,以上代码可以实现,不妥之处,欢迎指正。
这个案例解决了一个系统中正真减少创建SimpleDateFormat对象的开销。好思想好思路
43 楼
chenzehe
2012-11-29
pouyang 写道
pouyang 写道
引用
每个线程第一次获取都会初始化一次,你的实践验证能力很好,可能我开始描述的不是很清晰;
每一个线程初始化一次,每一个线程自身维护一个map,空间上占用内存较多,但不需要同步节约了时间;
每一个线程初始化一次,每一个线程自身维护一个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了,而且多并发也不会出问题。
是这样的,ThreadLocal相当于保存当前线程的变量,如果一个线程调用format方法N次把DateFormat对象缓存到当前线程里就很有必要了,但是我觉得下面这个方法就没必要用synchronized来同步了:
private static ThreadLocal threadLocal = new ThreadLocal() {
protected synchronized Object initialValue() {
return new SimpleDateFormat(DATE_FORMAT);
}
};
42 楼
gtssgtss
2011-03-12
Jony.Hwong 写道
其实楼主贴出来的使用ThreadLocal的解决方案,依然没有解决SimpleDateFormat创建开销昂贵的问题。
因为ThreadLocal的作用是:使得同一个线程共享一个实例,不同线程使用各自的实例。
对于多线程的情况,依然会创建很多个SimpleDateFormat实例。
因为ThreadLocal的作用是:使得同一个线程共享一个实例,不同线程使用各自的实例。
对于多线程的情况,依然会创建很多个SimpleDateFormat实例。
用ThreadLocal,n个线程创建n个SimpleDateFormat,不用ThreadLocal。n个线程创建kn个SimpleDateFormat
41 楼
Jony.Hwong
2011-03-12
其实楼主贴出来的使用ThreadLocal的解决方案,依然没有解决SimpleDateFormat创建开销昂贵的问题。
因为ThreadLocal的作用是:使得同一个线程共享一个实例,不同线程使用各自的实例。
对于多线程的情况,依然会创建很多个SimpleDateFormat实例。
因为ThreadLocal的作用是:使得同一个线程共享一个实例,不同线程使用各自的实例。
对于多线程的情况,依然会创建很多个SimpleDateFormat实例。
40 楼
分离的北极熊
2010-09-14
pengjunwu 写道
分离的北极熊 写道
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 以时间换空间
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只是为了避免了将这个对象作为参数传递的麻烦
既然是2个线程,而ThreadLocal是保证同一线程下获取的对象唯一,2个线程要拿到同一个对象?你看它的普通写法,也没有涉及任何同步,这就和第一个例子矛盾了
比如我现在管理Connection ,2个线程,我同事拿这一个连接,那不死的很惨。
39 楼
niumd
2010-09-12
pouyang 写道
实在是以空间换了时间,解惑了,谢谢了
我现在积分只有20分,30分才可以投票,要不给你来个精华了
我现在积分只有20分,30分才可以投票,要不给你来个精华了
分享下自己的认识,主要在于交流,共同提高;
38 楼
pouyang
2010-09-10
实在是以空间换了时间,解惑了,谢谢了
我现在积分只有20分,30分才可以投票,要不给你来个精华了
我现在积分只有20分,30分才可以投票,要不给你来个精华了
37 楼
pouyang
2010-09-10
pouyang 写道
引用
每个线程第一次获取都会初始化一次,你的实践验证能力很好,可能我开始描述的不是很清晰;
每一个线程初始化一次,每一个线程自身维护一个map,空间上占用内存较多,但不需要同步节约了时间;
每一个线程初始化一次,每一个线程自身维护一个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了,而且多并发也不会出问题。
36 楼
pengjunwu
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”);
35 楼
zhang34082
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(); } } }
34 楼
pengjunwu
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 以时间换空间
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只是为了避免了将这个对象作为参数传递的麻烦
33 楼
pengjunwu
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本身就是非线程安全的 这个在多线程环境下还是有问题吧
32 楼
msi110
2010-09-09
貌似有点像单例模式中的懒加载................
31 楼
liujun999999
2010-09-09
zhang34082 写道
其实楼主那个例子对于多个线程访问来说,是没办法实现共享同一个实例,在ThreadLocalMap里面,是以线程为Key,也就是说多线程时,会实例化多个SimpleDateFormat。如果真的需要共享同一个实例,我觉得可以用单例模式来实现
单例模式必须同步,因为SimpleDateFormat不是线程安全的,有性能瓶颈
另外,楼主的例子有个问题,就是,SimpleDateFormat的pattern必须是唯一的,实际项目中可能需要各种各样的pattern,怎么处理这个问题?
发表评论
-
Java8学习笔记:Lambda表达式定义
2016-12-03 22:17 21Lambda定义 Lambda是可传递的匿名函数,它没有名 ... -
浅析BlockingQueue实现
2013-05-21 22:16 1640BlockingQueue BlockingQueue ... -
Jboss Netty源码分析一
2011-05-14 10:29 8502本文采用版 ... -
浅谈Selector创建机制
2011-05-10 14:33 1404前段时间阅读mina源码时,理解Selector实例化机制细 ... -
探究Struts2运行机制:StrutsPrepareAndExecuteFilter 源码剖析
2010-11-30 23:29 6343作者:niumd blog:http://ari.i ... -
【翻译】Spring Integration参考手册中文版
2010-11-26 14:36 6443工作主要基于商业中间件TIBCO做EA ... -
深入浅出ThreadLocal
2010-09-07 14:16 5524一、ThreadLocal概述 学习JDK中的 ... -
八个改善Java遗留系统的技巧
2010-09-06 09:32 562你没看错,就是这个题目:即使是Java系统也会变成“ ... -
高并发下NIO socket消息超时机制的探讨
2010-09-02 12:31 2737去年参与项目的 ... -
项目重构之命令模式
2010-06-13 10:23 1035项目中有个业务处理类大小117K,代码2700行,看此 ... -
Java TCP/IP Socket编程
2010-06-11 13:00 592最想想写一篇关于java socket编程的文章,特此占位,提 ... -
Spring JDBC Framework详解——批量JDBC操作、ORM映射
2010-03-17 21:30 3631作者:niumd,转载请注 ... -
翻译:Apache MINA User Guide】Chapter 2 - Basics 之客户端应用程序
2009-12-28 08:40 1066作者:niumd blog:http://ari.iteye ... -
【翻译:Apache MINA User Guide】Chapter 2 - Basics
2009-12-26 15:08 1210本章节我们将详细介绍基于Apache MINA的C/S ... -
【翻译:Apache MINA User Guide 】 Chapter 1
2009-12-26 14:41 1045作者:niumd blog:http://ari.iteye ... -
MyEclipse8 GA 下载地址 注册码 优化指南
2009-12-26 10:51 3676官方网站 ... -
基本类型,字符串转字节数组
2009-09-22 22:39 778接触SMPP协议,SMPP协议中的消息分为消息头、消 ... -
byte与16进制互转
2009-09-22 22:27 1090private static final char[] kD ... -
jxl导出excel:java.lang.ArrayIndexOutOfBoundsExceptio
2009-05-14 18:54 4101先大致说下项目:公司的一个项目小功能需要上传16 ... -
设计模式精解【中文版书签版】
2009-04-15 12:43 1308工作之余研究java设计模式、个人感觉设计模式精解这 ...
相关推荐
在提供的"ThreadLocal示例"压缩包中,可能包含了一些具体的代码示例,展示如何在实际项目中运用ThreadLocal。通过查看这些示例,你可以更深入地理解ThreadLocal的工作方式以及如何在你的代码中有效地利用它。
以上就是关于ThreadLocal的基本概念、使用方法、生命周期管理和实际应用示例的详细解释。了解并熟练掌握ThreadLocal可以帮助我们编写出更高效、更安全的多线程代码。在Java并发编程中,ThreadLocal是一个不可或缺的...
#### 五、Java API中的ThreadLocal示例 下面的示例展示了一个更具体的场景——为每个线程分配一个唯一的标识符。 ```java import java.util.concurrent.atomic.AtomicInteger; public class ThreadId { // ...
以下是一个完整的ThreadLocal示例,演示了如何在一个Runnable实例中使用ThreadLocal: ```java public class ThreadLocalExample { public static class MyRunnable implements Runnable { private ThreadLocal...
下面是一个简单的ThreadLocal示例: ```java public class ThreadLocalTest { private static ThreadLocal<Integer> threadLocal = new ThreadLocal() { protected synchronized Integer initialValue() { ...
例如,以下是一个简单的ThreadLocal示例,用于生成线程安全的序列号: ```java private static ThreadLocal<Integer> seqNum = new ThreadLocal() { @Override protected Integer initialValue() { return 0; }...
【Android中的ThreadLocal使用示例】 ThreadLocal是Java(因此也适用于Android)中的一种特殊变量类型,它允许每个线程拥有变量的独立副本。在多线程环境下,ThreadLocal可以帮助我们实现线程间的隔离,避免数据...
1. **数据库连接管理**:如上文的Hibernate示例,通过`ThreadLocal`管理每个线程的数据库连接,确保每个线程拥有独立的连接资源,避免了资源竞争。 2. **事务处理**:在事务管理中,`ThreadLocal`可以用于维护每个...
下面是一个简单的`ThreadLocal`使用示例: ```java public class ThreadLocalDemo { public static void main(String[] args) { ThreadLocal<String> threadLocal = new ThreadLocal(); // 在主线程中设置值 ...
下面是一个ThreadLocal的简单应用示例: ```java public class ConnectionHolder { private static final ThreadLocal<Connection> connectionHolder = new ThreadLocal(); public static void setConnection...
这个压缩包 "Quartz-ThreadLocal.rar" 内含的学习资源很可能是关于如何在 Quartz 调度器中结合使用 ThreadLocal 的示例。 Quartz 的核心功能包括: 1. **作业与触发器**:在 Quartz 中,任务被称为“作业”(Job)...
深入理解ThreadLocal工作原理及使用示例 ThreadLocal是Java提供的一种解决多线程程序并发问题的工具类,自JDK1.2版本以来提供了java.lang.ThreadLocal类。ThreadLocal的主要作用是为每个使用该变量的线程提供独立的...
下面是一个简单的ThreadLocal使用示例: ```java public class MainActivity extends AppCompatActivity { private ThreadLocal<Person> mThreadLocal = new ThreadLocal(); private Person mPerson = new Person...
#### 四、ThreadLocal的简单实现示例 为了更直观地理解`ThreadLocal`的工作原理,我们可以创建一个简单的`SimpleThreadLocal`类。下面是一个基本的实现: ```java public class SimpleThreadLocal<T> { private ...
通过上述分析,我们可以看出`ThreadLocal.rar`中的代码示例可能涉及了如何创建、设置、获取和清理ThreadLocal变量,以及如何在父子线程之间利用ThreadLocal进行通信。学习并理解ThreadLocal的原理和使用方法对于编写...
为了深入理解ThreadLocal的实现,我们可以构建一个简化的`SimpleThreadLocal`类示例,如代码清单1所示,其中包含了一个`Map`成员变量,用于存储线程及其对应的变量副本。 #### 结论 ThreadLocal作为一种独特的多...
通过Demo示例介绍了如何利用ThreadLocal实现线程间的隔离,使各个线程可以拥有独立的变量副本而不互相干扰。文章详细讲解了ThreadLocal中关键方法get、set、remove的操作流程,并解释了其中使用弱引用作为键的Entry...
ThreadLocal 是 Java 中一个非常重要的工具类,它主要用于在多线程环境中为每个线程创建独立的、私有的变量副本,避免了线程之间数据共享带来的复杂性和安全性问题。了解 ThreadLocal,首先需要掌握 Java 中的四种...