zoukankan      html  css  js  c++  java
  • 浅析 ThreadLocal

    一、ThreadLocal类说明

      ThreadLocal,很容易让人望文生义,直译"本地线程"。ThreadLocal不是一个thread,是thread的局部变量。使用ThreadLocal维护变量时,它为每个使用它的线程提供独立的线程副本,每个线程可以改变自己的副本,而不会影响到其它线程对应的副本。

      从线程的角度看,目标变量就象是线程的本地变量,这也是类名中"Local"所要表达的意思。

    二、ThreadLocal常用方法

    ①、当前变量的初始值
      protected T initialValue() 

    源码 如下,该方法是一个protected方法,显然是为了让子类覆盖而设计的。该方法是在get|set会调用,且只会被调一次。ThreadLocal缺省实现直接返回null

    protected T initialValue() {
            return null;
        }

    ②、设置当前线程的线程局部变量值

      void set(T value)

    ③、返回当前线程的线程局部变量值

      public T get()

    ④、删除当前线程的线程局部变量值,节省内存的占用。

      public void remove()

    三、下面我们通过一个具体例子,看看ThreadLocal的具体用法---Id生成器

    id生成器

    public interface Sequence {
    
        public int getId();
    }
    
    public class LocalSequenceImpl implements Sequence {
    
        private ThreadLocal<Integer> container = new ThreadLocal<Integer>() {
            
            @Override
            protected Integer initialValue() {
                return 0;
            };
        };
        
        @Override
        public int getId() {
            container.set(container.get() + 1);
            return container.get();
        }
    
    }

    线程

    public class ThreadLocalDemo extends Thread {
    
        private static Sequence obj;
    
        @SuppressWarnings("static-access")
        public ThreadLocalDemo(Sequence obj) {
            this.obj = obj;
        }
    
        public void run() {
            for (int i = 0; i < 3; i++) {
                System.out.println(Thread.currentThread().getName() + " : " + obj.getId());
            }
        }
    
        public static void main(String[] args) {
            
            Sequence obj = new LocalSequenceImpl();
            
            test(obj);
    
        }
    
        static void test(Sequence obj) {
    
            ThreadLocalDemo thread1 = new ThreadLocalDemo(obj);
    
            ThreadLocalDemo thread2 = new ThreadLocalDemo(obj);
    
            ThreadLocalDemo thread3 = new ThreadLocalDemo(obj);
    
            thread1.start();
            thread2.start();
            thread3.start();
        }
    }

    结果  : 

    Thread-0 : 1
    Thread-1 : 1
    Thread-1 : 2
    Thread-2 : 1
    Thread-2 : 2
    Thread-2 : 3
    Thread-1 : 3
    Thread-0 : 2
    Thread-0 : 3

    从上述结果,每个线程只会更改自己的局部变量,并没有发生互相干扰的情况。可以看出来,ThreadLocal为使用它的线程提供独立的副本。

    不信?我们下面再通过一个不使用ThreadLocal来验证下。

    public class SequenceImpl implements Sequence {
        
        private static int id = 0;
    
        @Override
        public int getId() {
            id = id + 1;
            return id;
        }
    }

     结果

    Thread-0 : 2
    Thread-0 : 4
    Thread-0 : 5
    Thread-2 : 3
    Thread-2 : 6
    Thread-2 : 7
    Thread-1 : 1
    Thread-1 : 8
    Thread-1 : 9

     仔细观察下结果,三个线程同时访问一个局部变量id,发生了互相干扰的情况。

    四、拓展

    看下ThreadLocal源码,底层是用Map实现的。我们可以自己设计一个ThreadLocal

    public class MyThreadLocal<T> {
    
        private Map<Thread, T> container = Collections.synchronizedMap(new HashMap<>());
        
        public void set(T value) {
            container.put(Thread.currentThread(), value);
        }
        
        public T get() {
            Thread currentThread = Thread.currentThread();
            T value = container.get(Thread.currentThread());
            if(value == null && !container.containsKey(currentThread)) {
                value = initialValue();
                container.put(Thread.currentThread(), value);
            }
            return value;
        }
        
        protected T initialValue() {
            return null;
        }
    }
  • 相关阅读:
    BZOJ2301——莫比乌斯&&整除分块
    2019HDU多校第五场A fraction —— 辗转相除法|类欧几里得
    AKS素性检测
    2019牛客多校B generator 1——十进制快速幂
    BZOJ 3884——欧拉降幂和广义欧拉降幂
    libevent HTTP client 的实现
    google proto buffer安装和简单示例
    setenv LD_LIBRARY_PATH
    Centos6.4下安装protobuf及简单使用
    lrzsz
  • 原文地址:https://www.cnblogs.com/chenmo-xpw/p/5598736.html
Copyright © 2011-2022 走看看