美文网首页
ThreadLocal

ThreadLocal

作者: Jokerone_ | 来源:发表于2017-05-07 16:49 被阅读0次

    面试过程中,问到了使用ThreadLocal时要注意的事项。当时脑袋一懵,只是知道ThreadLocal使得变量线程局部化,每个线程都有自己的变量,从而使得线程安全。
    下面说一些ThreadLocal使用时要注意的问题。

    线程池使用过程中,ThreadLocal读取到的变量可能是上次任务执行完成的脏数据。

    实例代码如下:

    package com.hao.laker.study.myconcurrent;
    
    import java.util.concurrent.*;
    
    /**
     * Created by haojiahong on 17/5/7.
     */
    public class ThreadLocalTest {
        private static ExecutorService executor = Executors.newFixedThreadPool(1);
        public static ThreadLocal<String> threadLocal = new ThreadLocal<>();
    
        //线程池中线程通过一个任务对ThreadLocal变量赋值。
        private static class SetTask implements Callable<String> {
            private Integer i;
    
            public SetTask(Integer i) {
                this.i = i;
            }
    
    
            @Override
            public String call() throws Exception {
                threadLocal.set(Thread.currentThread().getName() + "的值为:" + i);
                return threadLocal.get();
            }
        }
    
        //线程池中线程通过一个任务获取此线程本地化的ThreadLocal变量值
        private static class GetTask implements Callable<String> {
    
            @Override
            public String call() throws Exception {
                System.out.println(Thread.currentThread().getName());
                return threadLocal.get();
            }
        }
    
    
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            SetTask setTask = new SetTask(2);
            GetTask getTask = new GetTask();
            //给线程池中的这个线程设置了ThreadLocal值
            Future<String> result = executor.submit(setTask);
            System.out.println(result.get());
    
            //新的任务拿到了以前任务设置的值,这是脏数据。
            Future<String> getResult = executor.submit(getTask);
            System.out.println(getResult.get());
    
            threadLocal.set(Thread.currentThread().getName() + "的值为:" + 5);
            System.out.println(threadLocal.get());
    
            Future<String> getResult2 = executor.submit(getTask);
            System.out.println(getResult2.get());
        }
    
    }
    
    package com.hao.laker.study.myconcurrent;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * Created by haojiahong on 17/5/23.
     */
    public class ThreadLocal2Test {
        private static ExecutorService executor = Executors.newFixedThreadPool(1);
    
        public static class MyRunnable implements Runnable {
    
            private ThreadLocal threadLocal = new ThreadLocal();
    
            @Override
            public void run() {
                System.out.println(Thread.currentThread());
                threadLocal.set((int) (Math.random() * 100D));
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
    
                }
                System.out.println(Thread.currentThread());
                System.out.println(threadLocal.get());
            }
        }
    
        public static class MyRunnable2 implements Runnable {
    
            private ThreadLocal threadLocal = new ThreadLocal();
    
            @Override
            public void run() {
                System.out.println(Thread.currentThread());
                threadLocal.set((int) (Math.random() * 100D));
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
    
                }
                System.out.println(Thread.currentThread());
                System.out.println(threadLocal.get());
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            MyRunnable sharedRunnableInstance = new MyRunnable();
    //        Thread thread1 = new Thread(sharedRunnableInstance);
    //        Thread thread2 = new Thread(sharedRunnableInstance);
    //        thread1.start();
    //        thread2.start();
            executor.execute(sharedRunnableInstance);
            executor.execute(sharedRunnableInstance);
    
            sharedRunnableInstance = null;
    
            System.gc();
    
            Thread.sleep(1000);
    
            MyRunnable2 shared2Instance = new MyRunnable2();
            executor.execute(shared2Instance);
    
        }
    }
    
    

    当ThreadLocal对象存储在各个任务类中时,如果任务执行完没有对ThreadLocal进行remove操作,那么线程池中的线程ThreadLocalMap会一直保持这个已这个任务的ThreadLocal为key的value值。从而造成内存泄露。

    ThreadLocal可能造成数据泄露

    这个问题还不是太清楚。简单的理解就是,引用ThreadLocal的对象被垃圾回收了,即ThreadLocal被垃圾回收了,但是线程池中的线程没有回收的话,线程局部化的ThreadLocal.ThreadLocalMap不会被垃圾回收,造成了内存泄露。
    更通俗的理解是:线程池中的ThreadLocal如果在线程任务执行完后不进行remove操作,那么ThreadLocal引用的对象将不会被垃圾回收,造成内存泄露。

    深入分析 ThreadLocal 内存泄漏问题
    Java多线程9:ThreadLocal源码剖析

    相关文章

      网友评论

          本文标题:ThreadLocal

          本文链接:https://www.haomeiwen.com/subject/wzgutxtx.html