• 《Java 并发编程》ThreadLock详解


    前言

    在并发开发的过程中,我们都知道需要保证共享资源的的读写有序。加锁是我们比较常用的一种方式。ThreadLock则是从另外一个角度出发,每一个线程都独立资源,这样同样可以解决资源的问题。这样讲可能不是很好理解,下面我们通过案例来说明这个情况。

    案例

    我们在使用日期格式转换的时候,会出现日期转换出错,或者日期不是自己想要的结果。

    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class ThreadLockTest {
        private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
        public static class ParseDate implements Runnable {
    
            int i = 0;
    
            public ParseDate(int i) {
                this.i = i;
            }
    
            @Override
            public void run() {
                try {
                    Date f = sdf.parse("2017-01-15 15:22:" + i/60);
                    System.out.println(i + ":" + f);
                } catch (ParseException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public static void main(String[] args) {
            ExecutorService es = Executors.newFixedThreadPool(10);
            for (int i = 0; i < 1000; i ++) {
                es.execute(new ParseDate(i));
            }
        }
    }

    运行结果:

    SimpleDateFormat 本身不是线程安全的类,所以在这样的情况,我们直接这样使用是会出现问题的。

    当然如果我们直接在线程里面new SimpleDateFormat来处理也是可以的,但是这样的话,因为日期处理一般系统都比较多的一种操作,每次都创建对象,就更加容易让Jvm出现OOM的情况。还有一种方式,就是每一个线程里面我们都创建一个SimpleDateFormat来处理线程内部的日期格式化。我们来一起看看如何实现:

    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class ThreadLockTest1 {
    
        static ThreadLocal<SimpleDateFormat> t1 = new ThreadLocal<>();
    
        public static class ParseDate implements Runnable {
    
            int i = 0;
    
            public ParseDate(int i) {
                this.i = i;
            }
    
            @Override
            public void run() {
                try {
                    if (t1.get() == null) {   // 当前线程没有SimpleDateFormat对象,则创建一个
                        t1.set(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
                    }
                    Date f = t1.get().parse("2017-01-15 15:22:" + i/60);
                    System.out.println(i + ":" + f);
                } catch (ParseException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public static void main(String[] args) {
            ExecutorService es = Executors.newFixedThreadPool(10);
            for (int i = 0; i < 1000; i ++) {
                es.execute(new ParseDate(i));
            }
        }
    }

    运行结果:

    每一线程执行都正常了。

    原理

    看看我们用到的两个方法的源码实现逻辑:

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

    将对象存放到本地线程中。

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocal.ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocal.ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

    获取对象的时候也从本地线程中获取。

    重点:我们现在系统基本都使用线程池,如果你设置到线程里面的对象会导致GC无法回收。

    如需及时回收对象,则可以使用

    ThreadLocal.remove();    -- 清空线程中的对象信息。

    总结

    该功能用处并不会很广,比较小众的使用场景,了解即可。

    参考:https://blog.csdn.net/mzh1992/article/details/62230075

    This moment will nap, you will have a dream; But this moment study,you will interpret a dream.
  • 相关阅读:
    firefox native extension -- har export trigger
    配置jenkins slave 问题,ERROR: Couldn't find any revision to build. Verify the repository and branch configuration for this job.
    尝试用selenium+appium运行一个简单的demo报错:could not get xcode version. /Library/Developer/Info.plist doest not exist on disk
    ruby 除法运算
    ERB预处理ruby代码
    ruby self.included用法
    ruby include和exclude区别
    symfony安装使用
    解决git中文乱码
    读《微软的软件测试之道》有感(上)
  • 原文地址:https://www.cnblogs.com/jssj/p/14373766.html
Copyright © 2020-2023  润新知