public static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss");
public static Date parse(String stringDate) throws ParseException {
return simpleDateFormat.parse(stringDate);
}
public static void main(String[] args) throws ParseException {
for (int i = 0; i < 3; i++) {
new Thread(()->{
Date parse = null;
try {
parse = parse("2021-12-12 12:12:12");
System.out.println(parse);
} catch (ParseException e) {
e.printStackTrace();
}
}).start();
}
}
上述代码,如果是直接使用new,不会出现,如果是使用static格式,会导致出现数据错乱。
Exception in thread "Thread-0" Exception in thread "Thread-1" java.lang.NumberFormatException: empty String
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1842)
at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
at java.lang.Double.parseDouble(Double.java:538)
at java.text.DigitList.getDouble(DigitList.java:169)
at java.text.DecimalFormat.parse(DecimalFormat.java:2089)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:2162)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
at java.text.DateFormat.parse(DateFormat.java:364)
at com.jia.threadlocal.ThreadlocalDateUtils.parse(ThreadlocalDateUtils.java:16)
at com.jia.threadlocal.ThreadlocalDateUtils.lambda$main$0(ThreadlocalDateUtils.java:26)
at java.lang.Thread.run(Thread.java:748)
java.lang.NumberFormatException: empty String
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1842)
at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
at java.lang.Double.parseDouble(Double.java:538)
at java.text.DigitList.getDouble(DigitList.java:169)
at java.text.DecimalFormat.parse(DecimalFormat.java:2089)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:2162)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
at java.text.DateFormat.parse(DateFormat.java:364)
at com.jia.threadlocal.ThreadlocalDateUtils.parse(ThreadlocalDateUtils.java:16)
at com.jia.threadlocal.ThreadlocalDateUtils.lambda$main$0(ThreadlocalDateUtils.java:26)
at java.lang.Thread.run(Thread.java:748)
Tue Jan 12 12:12:12 CST 2021
而java手册也描述了,simpleDataFormat其实是一个线程不安全的类,在并发编程的环境下。
具体原因就是SimpleDateFormat底层依赖的是Calendar。
1.通过加锁,syn进行标识。
2.使用threadlocal进行将每个线程存储一份自己独享的simpleDateFormat对象。
public static final ThreadLocal<SimpleDateFormat> threadLocalDate = ThreadLocal.withInitial(()-> new SimpleDateFormat("yyyy-mm-dd HH:mm:ss"));
public static Date threadLocalParse (String date) throws ParseException {
return threadLocalDate.get().parse(date);
}
public static void main(String[] args) throws ParseException {
for (int i = 0; i < 3; i++) {
new Thread(()->{
Date parse = null;
try {
parse = threadLocalParse("2021-12-12 12:12:12");
System.out.println(parse);
} catch (ParseException e) {
e.printStackTrace();
} finally {
threadLocalDate.remove(); //记得remove
}
}).start();
}
}
thread类包含threadLocal
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
而threadlocal包含threadlocalMap
static class ThreadLocalMap {
}
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue(); //创建map
}
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
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);
}
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
MyObject object = new MyObject();
System.out.println(object);
System.gc();
Thread.sleep(1000);
System.out.println(object);
SoftReference<MyObject> myObjectSoftReference = new SoftReference<>(new MyObject());
System.out.println("内存够用"+myObjectSoftReference);
System.gc();
Thread.sleep(1000);
System.out.println("内存够用"+myObjectSoftReference);
为了解决线程内数据跨方法类的调用,使用类threadlocal,具体就是thread包含一个threadlocal,而threadlocal内部包含一个threadlocalMap对象。key为this (threadlocal) vaule为对应的值。为了保证引用可以被删一个是程序内部使用弱引用,而是通过程序员remove()进行维护删除,以及来保证内存泄露。