将 ThreadLocal 用于 JDBC 连接是一种好习惯吗?

Is it good practice to use ThreadLocal for JDBC Connections?

我不确定我是否了解 ThreadLocals。有时您会读到,一种常见的做法是将 JDBC 连接作为 ThreadLocals,以便每个线程都获得自己的连接副本。假设下面的套接字是一个 JDBC 连接。然后我做:

public ThreadLocalSocket() throws IOException {

    Runnable runnable = new Runnable() {

        ThreadLocal<Socket> threadLocal = new ThreadLocal<>();
        @Override
        public void run() {
            try {
                threadLocal.set(new Socket("www.google.com", 80));
            } catch (IOException e) {e.printStackTrace();}

            Thread thread = Thread.currentThread();
            for (int j = 0; j < 10 ; j++) {

                Socket sock = threadLocal.get();
                if (thread.getName().equals("t1")) {
                    if (!sock.isClosed()) {
                        try {
                            sock.close();
                        } catch (IOException e) {e.printStackTrace();}
                    }
                }
                System.out.println("Thread: " + thread.getName()+  ", is closed? " + sock.isClosed() + ", sock hashCode = " + sock.hashCode());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {e.printStackTrace();}
            }
        }
    };

    Thread t1 = new Thread(runnable);
    t1.setName("t1");
    Thread t2 = new Thread(runnable);
    t2.setName("t2");

    t1.start();
    t2.start();
}

为什么我不能在没有 ThreadLocals 的情况下简单地做到这一点?以下代码片段中的行为与上面的代码示例完全相同:

public ThreadLocalSocket() throws IOException {

    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            Socket socket = null;
            try {
                socket = new Socket("www.google.com", 80);
            } catch (IOException e) {e.printStackTrace();}

            Thread thread = Thread.currentThread();
            for (int j = 0; j < 10 ; j++) {
                if (thread.getName().equals("t1")) {
                    if (!socket.isClosed()) {
                        try {
                            socket.close();
                        } catch (IOException e) {e.printStackTrace();}
                    }
                }
                System.out.println("Thread: " + thread.getName()+  ", is closed? " + socket.isClosed() + ", socket hashCode = " + socket.hashCode());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {e.printStackTrace();}
            }
        }
    };

    Thread t1 = new Thread(runnable);
    t1.setName("t1");
    Thread t2 = new Thread(runnable);
    t2.setName("t2");

    t1.start();
    t2.start();
}

是的,在您的用例中,它毫无用处。

javadoc of ThreadLocal 状态

ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).

典型的用例是将 ThreadLocal 作为一种全局成员,这样您就不必传递引用值。您通过一些 static 方法公开它,您编写的每一段代码都可以访问它,而不必每次都注入它。


例如,Spring MVC 提供了 class RequestContextHolder,它保留对 ServletRequest 对象的 ThreadLocal 引用。由于 servlet 容器通常(即非异步模式)通过在单个线程中处理请求来工作,而不是必须将 ServletRequest 传递给处理流程中的每个组件,Spring 设置它一旦在 RequestContextHolder 中,其他组件就可以通过 static 辅助方法(实际上只是请求的属性)访问它。