多线程学习总结----ThreadLocal及InheritableThreadLocal介绍

ThreadLocal及InheritableThreadLocal

1.什么是ThreadLocal?

简单来说,这时JDK提供的.当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本.

代码演示:

package com.my.thread;
   public class ThreadDemo2 {
       private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
           @Override
           public Integer initialValue() {
               return 0;
           }
       };
       static class ThreadDemo implements Runnable {
           @Override
           public void run() {
               for (int i = 0; i < 1000; i++) {
                   threadLocal.set(threadLocal.get() + 1);
               }
               System.out.println("thread :" + Thread.currentThread().getId() + " is" + threadLocal.get());
           }
       }
       public static void main(String[] args) {
           for (int i = 0; i < 10; i++) {
               new Thread(new ThreadDemo()).start();
           }
       }
   }
结果:
    thread :11 is1000
    thread :12 is1000
    thread :13 is1000
    thread :14 is1000
    thread :17 is1000
    thread :16 is1000
    thread :15 is1000
    thread :18 is1000
    thread :19 is1000
    thread :20 is1000

从上面的结果可以看出,输出都是1000.说明线程之间运算是没有影响的.并没因为共享属性而造成运算混乱问题.

ThreadLocal原理剖析 (部分代码)

ThreadLocal.ThreadLocalMap threadLocals = null;
        protected T initialValue() {
            return null;
        }
        void createMap(Thread t, T firstValue) {
            t.threadLocals = new ThreadLocalMap(this, firstValue);
        }
        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();
        }
        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;
        }
        public void set(T value) {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null)
                map.set(this, value);
            else
                createMap(t, value);
        }
        ThreadLocalMap getMap(Thread t) {
           return t.threadLocals;
        }

总结

从最开始我们使用ThreadLocal的时候,可以重写initialValue()方法,给其赋初始值.
1.如果先要调用set(T value)方法设置值,这时会先使用getMap(t)获取当前线程ThreadLocalMap集合对象.
判断如果为null,会通过createMap(Thread t, T firstValue)来创建和赋值.
如果不为null,说明已经有了ThreadLocalMap集合,直接往集合里添加即可.
2.如果先调用get()方法,也会先去getMap(t)获取当前线程的ThreadLocalMap集合对象.
如果不为null,这从ThreadLocalMap集合中获取.
如果为null,则会执行setInitialValue()方法,并通过createMap(Thread t, T firstValue)来创建和赋值.

到这里,也应该明白了吧.我们在定义的ThreadLocal对象时,并没有为其线程创建ThreadLocalMap集合.而是在调用get()和set方法时,去判断创建的.
所以也就是每个线程都有自己的一个ThreadLocalMap集合,用于自己线程使用.

那么当线程结束又是怎么处理的呢?

private void exit() {
            if (group != null) {
                group.threadTerminated(this);
                group = null;
            }
            target = null;
            threadLocals = null;
            inheritableThreadLocals = null;
            inheritedAccessControlContext = null;
            blocker = null;
            uncaughtExceptionHandler = null;
        }

从上面可以看到,如果线程结束会调用exit()方法.会执行一句threadLocals = null;这时ThreadLocalMap集合就会空引用了,等待GC前来清理销毁.
这样处理完全没问题,但是用在线程池里面就不一样.

    package com.my.thread;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;

    public class ThreadLocalTest {

    	//创建一个Integer本地线程变量
    	private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
    		@Override
    		public Integer initialValue() {
    			return 0;
    		}
    	};
    	static class ThreadDemo implements Runnable {
    		@Override
    		public void run() {
    			for (int i = 0; i < 1000; i++) {
    				threadLocal.set(threadLocal.get() + 1);
    			}
    			System.out.println("thread :" + Thread.currentThread().getId() + " is" + threadLocal.get());
    			//threadLocal.remove();
    		}
    	}
    	public static void main(String[] args) {
    		ExecutorService executorService= Executors.newFixedThreadPool(5);
    		for (int i = 0; i < 10; i++) {
    			executorService.submit(new Thread(new ThreadDemo()));
    		}
    	}

    }

结果:
    thread :12 is1000
    thread :16 is1000
    thread :18 is1000
    thread :16 is2000
    thread :16 is3000
    thread :16 is4000
    thread :18 is2000
    thread :12 is2000
    thread :14 is1000
    thread :20 is1000

可以看出来,同一个线程时累加的.这也是因为在线程池的线程在执行完后,没有被杀死,而是又回到线程次中等待下一个任务.
当我们把//threadLocal.remove();的注释打开后,结果就如下面所示了。

结果:
       thread :14 is1000
       thread :14 is1000
       thread :14 is1000
       thread :14 is1000
       thread :12 is1000
       thread :14 is1000
       thread :12 is1000
       thread :16 is1000
       thread :18 is1000
       thread :20 is1000

可以看到,结果都是1000.所以在线程池中使用ThreadLocal时,只要在使用完后清空ThreadLocal的值,就可以正常使用了.

2.InheritableThreadLocal

为什么要用这个InheritableThreadLocal?

首先,先说一下需求场景.当我们在一个线程A中启动线程B去执行一个操作,但是我们需要在线程B中使用到线程A的ThreadLocal中的一些值.这时就需要线程A的ThreadLocal可以传递到线程B中.

代码演示场景:

package com.my.thread;
public class ThreadLocalTest1 {
        private static  InheritableThreadLocal<Integer> inheritableThreadLocal = new  InheritableThreadLocal<Integer>();
        static class ThreadDemo implements Runnable {
        		@Override
        		public void run() {
        			for (int i = 0; i < 1000; i++) {
        				inheritableThreadLocal.set(inheritableThreadLocal.get() + 1);
        			}
        			System.out.println("thread :" + Thread.currentThread().getId() + " is" + inheritableThreadLocal.get());
        		}
        	}
        	public static void main(String[] args) {
        		inheritableThreadLocal.set(24);
        		for (int i = 0; i < 10; i++) {
        			new Thread(new ThreadDemo()).start();
        		}
        	}
        }
结果:
    thread :12 is1024
    thread :11 is1024
    thread :13 is1024
    thread :14 is1024
    thread :17 is1024
    thread :18 is1024
    thread :19 is1024
    thread :20 is1024
    thread :15 is1024
    thread :16 is1024

从上面可以看到,来自main线程的24被传递到了子线程中.最后打印1024

InheritableThreadLocal原理剖析

InheritableThreadLocal源码代码:

    public class InheritableThreadLocal<T> extends ThreadLocal<T> {
        protected T childValue(T parentValue) {
            return parentValue;
        }

        ThreadLocalMap getMap(Thread t) {
            return t.inheritableThreadLocals;
        }

        void createMap(Thread t, T firstValue) {
            t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
        }
    }

Thread类的部分代码:

    public class Thread implements Runnable {
        private void init(ThreadGroup g, Runnable target, String name,
                              long stackSize, AccessControlContext acc) {
                if (name == null) {
                    throw new NullPointerException("name cannot be null");
                }

                this.name = name.toCharArray();

                Thread parent = currentThread();
                SecurityManager security = System.getSecurityManager();
                if (g == null) {
                    /* Determine if it's an applet or not */

                    /* If there is a security manager, ask the security manager
                       what to do. */
                    if (security != null) {
                        g = security.getThreadGroup();
                    }

                    /* If the security doesn't have a strong opinion of the matter
                       use the parent thread group. */
                    if (g == null) {
                        g = parent.getThreadGroup();
                    }
                }

                /* checkAccess regardless of whether or not threadgroup is
                   explicitly passed in. */
                g.checkAccess();

                /*
                 * Do we have the required permissions?
                 */
                if (security != null) {
                    if (isCCLOverridden(getClass())) {
                        security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
                    }
                }

                g.addUnstarted();

                this.group = g;
                this.daemon = parent.isDaemon();
                this.priority = parent.getPriority();
                if (security == null || isCCLOverridden(parent.getClass()))
                    this.contextClassLoader = parent.getContextClassLoader();
                else
                    this.contextClassLoader = parent.contextClassLoader;
                this.inheritedAccessControlContext =
                        acc != null ? acc : AccessController.getContext();
                this.target = target;
                setPriority(priority);
                if (parent.inheritableThreadLocals != null)
                    this.inheritableThreadLocals =
                        ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
                /* Stash the specified stack size in case the VM cares */
                this.stackSize = stackSize;

                /* Set thread ID */
                tid = nextThreadID();
            }

    }

简单总结:

这里可以看到,InheritableThreadLocal实际上是继承了ThreadLocal,并重写了getMap(Thread t)和createMap(Thread t, T firstValue)和childValue(T parentValue)三个方法。值赋值map的集合是给了线程的inheritableThreadLocals变量。
而我们在看Thread源码的时候,我们就可以发现在init()方法中有一个:
Thread parent = currentThread();
this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
看到这里,相信你也明白了怎么回事了。

简单来说,在我们使用InheritableThreadLocal的时候.和使用ThreadLocal一样.只是子线程创建完线程并初始化中,会把当前父线程的inheritableThreadLocals传递到子线程中.这样就会在子线程中使用到父线程的数据了。

比如上述事例,就是在main线程中设置了线程main的inheritableThreadLocal中的值为24,在启动子线程的时候,就会把带有24的inheritableThreadLocal传递到子线程中。然后子线程再用对其inheritableThreadLocal(已有初始值24)做累加操作,最后结束得到1024

posted @ 2019-08-09 21:26  笑里笑外~  阅读(214)  评论(0编辑  收藏  举报