• ThreadLocal用法和实现原理(转)


    如果你定义了一个单实例的java bean,它有若干属性,但是有一个属性不是线程安全的,比如说HashMap。并且碰巧你并不需要在不同的线程中共享这个属性,也就是说这个属性不存在跨线程的意义。那么你不要sychronize这么复杂的东西,ThreadLocal将是你不错的选择。

    举例来说:

    package threadlocal;
    
    import java.util.TreeMap;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class TreadLocalTest {
    
        static ThreadLocal<TreeMap<String, Integer>> map0 = new ThreadLocal<TreeMap<String, Integer>>() {
            @Override
            protected TreeMap<String, Integer> initialValue() {
                System.out.println(Thread.currentThread().getName() + ":initialValue");
                return new TreeMap<String, Integer>();
            }
        };
    
        public void run() {
            ExecutorService es = Executors.newCachedThreadPool();
            for (int i = 0; i < 3; i++) {
                es.execute(new Task(i));
            }
            es.shutdown();
        }
    
        public static class Task implements Runnable {
            private int id;
    
            public Task(int id) {
                this.id = id;
            }
    
            public void run() {
                String threadName = Thread.currentThread().getName();
                System.out.println(threadName + ":start");
                TreeMap<String, Integer> map = map0.get();
                for (int i = 0; i < 5; i++) {
                    map.put(threadName + ":" + i, i + id * 100);
                    try {
                        Thread.sleep(100);
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName() + ':' + map);
            }
        }
    
        public static void main(String[] args) {
            TreadLocalTest test = new TreadLocalTest();
            test.run();
        }
    
    }

    输出解释;

    pool-1-thread-1:start
    pool-1-thread-1:initialValue
    pool-1-thread-2:start
    pool-1-thread-2:initialValue
    pool-1-thread-3:start
    pool-1-thread-3:initialValue
    pool-1-thread-1:{pool-1-thread-1:0=0, pool-1-thread-1:1=1, pool-1-thread-1:2=2, pool-1-thread-1:3=3, pool-1-thread-1:4=4}
    pool-1-thread-2:{pool-1-thread-2:0=100, pool-1-thread-2:1=101, pool-1-thread-2:2=102, pool-1-thread-2:3=103, pool-1-thread-2:4=104}
    pool-1-thread-3:{pool-1-thread-3:0=200, pool-1-thread-3:1=201, pool-1-thread-3:2=202, pool-1-thread-3:3=203, pool-1-thread-3:4=204}

    可以看到map0 虽然是个静态变量,但是initialValue被调用了三次,通过debug发现,initialValue是从map0.get处发起的。而且每个线程都有自己的map,虽然他们同时执行。

    进入Theadlocal代码,可以发现如下的片段;

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

    这说明ThreadLocal确实只有一个变量,但是它内部包含一个map,针对每个thread保留一个entry,如果对应的thread不存在则会调用initialValue。

    http://www.cnblogs.com/alphablox/archive/2013/01/20/2869061.html

    package threadlocal;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /*2015-9-15*/
    public class KeepObjectStatus {
    
        private int times = -1;
    
        public int getTimes() {
            return times;
        }
    
        public static void main(String[] args) {
            ExecutorService executor = Executors.newCachedThreadPool();
            executor.execute(new Task(2));
            executor.execute(new Task(5));
            executor.shutdown();
        }
    
        public void process() {
            if (times < 10) {
                times++;
            }
        }
    
        public static ThreadLocal<KeepObjectStatus> instance = new ThreadLocal<KeepObjectStatus>() {
    
            @Override
            protected KeepObjectStatus initialValue() {
                System.out.println(Thread.currentThread() + "  begin to init");
                return new KeepObjectStatus();
            }
    
        };
    
    }
    
    class Task implements Runnable {
    
        private int step;
    
        public Task(int step) {
            super();
            this.step = step;
        }
    
        @Override
        public void run() {
    
            KeepObjectStatus kos = KeepObjectStatus.instance.get();
            for (int i = 0; i < 10; i += step) {
                kos.process();
                System.out.println(String.format("%s==> Step:%s  %d", Thread.currentThread(), step, kos.getTimes()));
            }
        }
    
    }

    Output:

    Thread[pool-1-thread-1,5,main]  begin to init
    Thread[pool-1-thread-2,5,main]  begin to init
    Thread[pool-1-thread-2,5,main]==> Step:5  0
    Thread[pool-1-thread-1,5,main]==> Step:2  0
    Thread[pool-1-thread-2,5,main]==> Step:5  1
    Thread[pool-1-thread-1,5,main]==> Step:2  1
    Thread[pool-1-thread-1,5,main]==> Step:2  2
    Thread[pool-1-thread-1,5,main]==> Step:2  3
    Thread[pool-1-thread-1,5,main]==> Step:2  4




  • 相关阅读:
    推荐网页布局设计流程
    (IE6下)png透明问题分析及解决办法
    [收藏]几个常用的用正则表达式验证字符串的函数
    JavaScript 操作 Cookie
    javascript 获取控件的绝对位置
    Css命名规范
    掌握JavaScript语言的思想前提
    高效的Javascript 字符串操作类
    常用正则表达式
    简单的dom遍历
  • 原文地址:https://www.cnblogs.com/softidea/p/4178866.html
Copyright © 2020-2023  润新知