ThreadLocal

2017-05-07  本文已影响0人  Jokerone_

面试过程中,问到了使用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源码剖析

上一篇下一篇

猜你喜欢

热点阅读