Java的ThreadLocal

ThreadLocal

ThreadLocal 是 Java 中一个非常有用的类,它允许你创建线程局部变量。线程局部变量是指每个线程都有自己独立的变量副本,互不干扰。ThreadLocal 主要用于解决多线程环境下共享数据的线程安全性问题。

基本用法

创建 ThreadLocal 变量

ThreadLocal<Integer> threadLocalVariable = new ThreadLocal<>();

设置和获取线程局部变量:

threadLocalVariable.set(42); // 设置线程局部变量的值
int value = threadLocalVariable.get(); // 获取线程局部变量的值

初始值:

你可以通过覆盖 ThreadLocal 的 initialValue 方法来指定线程局部变量的初始值。例如:

ThreadLocal<Integer> threadLocalVariable = ThreadLocal.withInitial(() -> 0);

注意事项:

使用 ThreadLocal 时要小心内存泄漏,确保在不需要使用线程局部变量时及时清理。

在使用线程池时,注意线程复用可能导致线程局部变量的状态被共享。

ThreadLocal 是一个有助于在多线程应用程序中维护线程局部状态的重要工具,但它需要谨慎使用,以避免潜在的问题。确保理解其工作原理,并在需要的情况下适当使用它,可以提高多线程程序的性能和可维护性。

ThreadLocal理解

ThreadLocal是一个将在多线程中为每一个线程创建单独的变量副本的类; 当使用ThreadLocal来维护变量时, ThreadLocal会为每个线程创建单独的变量副本, 避免因多线程操作共享变量而导致的数据不一致的情况。#

最多的是session管理和数据库链接管理,这里以数据访问为例帮助你理解ThreadLocal:

class ConnectionManager {
    private static Connection connect = null;

    public static Connection openConnection() {
        if (connect == null) {
            connect = DriverManager.getConnection();
        }
        return connect;
    }

    public static void closeConnection() {
        if (connect != null)
            connect.close();
    }
}

据库管理类在单线程使用是没有任何问题的,在多线程中使用会存在线程安全问题:第一,这里面的2个方法都没有进行同步,很可能在openConnection方法中会多次创建connect;第二,多次进行closeConnection可能会导致空指针异常问题;第三,由于connect是共享变量,那么必然在调用connect的地方需要使用到同步来保障线程安全,因为很可能一个线程在使用connect进行数据库操作,而另外一个线程调用closeConnection关闭链接。

为了解决上述线程安全的问题,第一考虑:互斥同步

你可能会说,将这段代码的两个方法进行同步处理,并且在调用connect的地方需要进行同步处理,比如用Synchronized或者ReentrantLock互斥锁。

这里再抛出一个问题:这地方到底需不需要将connect变量进行共享?

事实上,是不需要的。假如每个线程中都有一个connect变量,各个线程之间对connect变量的访问实际上是没有依赖关系的,即一个线程不需要关心其他线程是否对这个connect进行了修改的。即改后的代码可以这样:

class ConnectionManager {
    private Connection connect = null;

    public Connection openConnection() {
        if (connect == null) {
            connect = DriverManager.getConnection();
        }
        return connect;
    }

    public void closeConnection() {
        if (connect != null)
            connect.close();
    }
}

class Dao {
    public void insert() {
        ConnectionManager connectionManager = new ConnectionManager();
        Connection connection = connectionManager.openConnection();

        // 使用connection进行操作

        connectionManager.closeConnection();
    }
}

这样处理确实也没有任何问题,由于每次都是在方法内部创建的连接,那么线程之间自然不存在线程安全问题。但是这样会有一个致命的影响:导致服务器压力非常大,并且严重影响程序执行性能。由于在方法中需要频繁地开启和关闭数据库连接,这样不仅严重影响程序执行效率,还可能导致服务器压力巨大。

这时候ThreadLocal登场了那么这种情况下使用ThreadLocal是再适合不过的了,因为ThreadLocal在每个线程中对该变量会创建一个副本,即每个线程内部都会有一个该变量,且在线程内部任何地方都可以使用,线程之间互不影响,这样一来就不存在线程安全问题,也不会严重影响程序执行性能。下面就是网上出现最多的例子:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class ConnectionManager {

    private static final ThreadLocal<Connection> dbConnectionLocal = new ThreadLocal<Connection>() {
        @Override
        protected Connection initialValue() {
            try {
                return DriverManager.getConnection("", "", "");
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return null;
        }
    };

    public Connection getConnection() {
        return dbConnectionLocal.get();
    }
}

ThreadLocal原理

如何实现线程隔离

主要是用到了Thread对象中的一个ThreadLocalMap类型的变量threadLocals, 负责存储当前线程的关于Connection的对象, dbConnectionLocal(以上述例子中为例) 这个变量为Key, 以新建的Connection对象为Value; 这样的话, 线程第一次读取的时候如果不存在就会调用ThreadLocal的initialValue方法创建一个Connection对象并且返回;

具体关于为线程分配变量副本的代码如下:

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap threadLocals = getMap(t);
    if (threadLocals != null) {
        ThreadLocalMap.Entry e = threadLocals.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

https://pdai.tech/md/java/thread/java-thread-x-threadlocal.html

相关推荐
何曾参静谧7 分钟前
「Py」Python基础篇 之 Python都可以做哪些自动化?
开发语言·python·自动化
Prejudices11 分钟前
C++如何调用Python脚本
开发语言·c++·python
Daniel 大东12 分钟前
idea 解决缓存损坏问题
java·缓存·intellij-idea
wind瑞18 分钟前
IntelliJ IDEA插件开发-代码补全插件入门开发
java·ide·intellij-idea
HappyAcmen18 分钟前
IDEA部署AI代写插件
java·人工智能·intellij-idea
马剑威(威哥爱编程)24 分钟前
读写锁分离设计模式详解
java·设计模式·java-ee
我狠狠地刷刷刷刷刷24 分钟前
中文分词模拟器
开发语言·python·算法
鸽鸽程序猿24 分钟前
【算法】【优选算法】前缀和(上)
java·算法·前缀和
修道-032325 分钟前
【JAVA】二、设计模式之策略模式
java·设计模式·策略模式
wyh要好好学习27 分钟前
C# WPF 记录DataGrid的表头顺序,下次打开界面时应用到表格中
开发语言·c#·wpf