ThreadLocal 优雅的编写多线程程序

1、什么是ThreadLocal?

友好式说法:ThreadLocal不是Thread(线程),是线程的局部变量,可以看成是线程的内部存储。每个线程中的ThreadLocal是互相独立的。

卧槽式说法:ThreadLocal就是线程里面的一个变量,把数据存到这个变量里面,在其它地方都可以调用。各个线程之间都是独立存储的,A线程只能访问A的ThreadLocal,B线程只能访问B的ThreadLocal。

ThreadLocal在编写Socket、Http服务的时候是非常常见的。


2、ThreadLocal的应用场景

请先看这样一段代码,没有ThreadLocal时的写法

public class App {
    public static void main(String[] args) {
        Thread threadA = new Thread(() -> {
            String username = "admin";
            Request request = new Request();
            request.username = username;
            doRequest(request);
        });

        Thread threadB = new Thread(() -> {
            String username = "root";
            Request request = new Request();
            request.username = username;
            doRequest(request);
        });

        threadA.start();
        threadB.start();
    }

    private static void doRequest(Request request) {
        request.username = "hello, " + request.username;
        doResponse(request);
    }

    private static void doResponse(Request request) {
        System.out.println(request.username);
    }

    static class Request {
        public String username;
    }
}

上面一段代码,有木有很像Servlet里面的http请求。username表示前端发送过来的参数,然后构造成Request对象,在doRequest处理逻辑,最后在doResponse中打印出数据。笔者就以这种很简陋的方式来模拟2个http请求,对比使用ThreadLocal和不使用的区别。

那么每次doRequest、doReponse都要把Request对象当做参数传递,如果处理方法超级多,是不是很烦。下面用ThreadLocal来解决这个问题。

public class App {
    public static void main(String[] args) {
        Thread threadA = new Thread(() -> {
            String username = "admin";
            Request request = new Request();
            request.username = username;

            Context.context().setRequest(request);

            doRequest();
        });

        Thread threadB = new Thread(() -> {
            String username = "root";
            Request request = new Request();
            request.username = username;

            Context.context().setRequest(request);

            doRequest();
        });

        threadA.start();
        threadB.start();
    }

    private static void doRequest() {
        Request request = Context.context().getRequest();
        request.username = "hello, " + request.username;
        doResponse();
    }

    private static void doResponse() {
        Request request = Context.context().getRequest();
        System.out.println(request.username);
    }

    static class Request {
        public String username;
    }

    static class Context {
        private static ThreadLocal<Context> threadLocal = ThreadLocal.withInitial(Context::new);

        public static Context context() {
            return threadLocal.get();
        }

        private Request request;

        public void setRequest(Request request) {
            this.request = request;
        }

        public Request getRequest() {
            return this.request;
        }
    }
}

从上面代码可以看到用ThreadLocal来存储数据,在当前线程的任何地方都是可以访问的,并且不同线程之间的数据是隔离的。threadA不会看到threadB的数据,反之亦然。

你会了吗?

本博客采用 知识共享署名-禁止演绎 4.0 国际许可协议 进行许可

本文标题:ThreadLocal 优雅的编写多线程程序

本文地址:https://dev-tang.com/post/2020/03/threadlocal.html