【Java并发】从simpleDateFormart聊聊threadlocal原理机制

SimpleDateFormats

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其实是一个线程不安全的类,在并发编程的环境下。
【Java并发】从simpleDateFormart聊聊threadlocal原理机制_第1张图片

源码分析

具体原因就是SimpleDateFormat底层依赖的是Calendar。
【Java并发】从simpleDateFormart聊聊threadlocal原理机制_第2张图片

【Java并发】从simpleDateFormart聊聊threadlocal原理机制_第3张图片

解决方案

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();
        }
    }

threadlocal原理解析

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 {
}

get方法

   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;
            }
        }

【Java并发】从simpleDateFormart聊聊threadlocal原理机制_第4张图片
【Java并发】从simpleDateFormart聊聊threadlocal原理机制_第5张图片
【Java并发】从simpleDateFormart聊聊threadlocal原理机制_第6张图片

引用类型

【Java并发】从simpleDateFormart聊聊threadlocal原理机制_第7张图片
【Java并发】从simpleDateFormart聊聊threadlocal原理机制_第8张图片

强引用

【Java并发】从simpleDateFormart聊聊threadlocal原理机制_第9张图片

        MyObject object = new MyObject();
        System.out.println(object);
        System.gc();
        Thread.sleep(1000);
        System.out.println(object);

软引用

【Java并发】从simpleDateFormart聊聊threadlocal原理机制_第10张图片

        SoftReference<MyObject> myObjectSoftReference = new SoftReference<>(new MyObject());
        System.out.println("内存够用"+myObjectSoftReference);

        System.gc();
        Thread.sleep(1000);

        System.out.println("内存够用"+myObjectSoftReference);

弱引用

在这里插入图片描述
【Java并发】从simpleDateFormart聊聊threadlocal原理机制_第11张图片

虚引用

【Java并发】从simpleDateFormart聊聊threadlocal原理机制_第12张图片
【Java并发】从simpleDateFormart聊聊threadlocal原理机制_第13张图片
【Java并发】从simpleDateFormart聊聊threadlocal原理机制_第14张图片

为什么使用弱引用

【Java并发】从simpleDateFormart聊聊threadlocal原理机制_第15张图片
【Java并发】从simpleDateFormart聊聊threadlocal原理机制_第16张图片
【Java并发】从simpleDateFormart聊聊threadlocal原理机制_第17张图片
【Java并发】从simpleDateFormart聊聊threadlocal原理机制_第18张图片

小结

为了解决线程内数据跨方法类的调用,使用类threadlocal,具体就是thread包含一个threadlocal,而threadlocal内部包含一个threadlocalMap对象。key为this (threadlocal) vaule为对应的值。为了保证引用可以被删一个是程序内部使用弱引用,而是通过程序员remove()进行维护删除,以及来保证内存泄露。

你可能感兴趣的:(#,并发编程,java,python,开发语言)