Java并发编程之threadLocal完整攻略
ThreadLocal是Java提供的一种线程封闭机制,可以实现线程间数据隔离。在并发编程中,线程间数据共享往往是很麻烦的问题,而ThreadLocal则可以帮助我们方便地解决这一问题。
ThreadLocal基本概念
以简单的方式来描述ThreadLocal,就是一个类似于Map的存储结构。不同之处在于,Map中的key是任何对象,而ThreadLocal的key值必须是当前线程内的对象。ThreadLocal提供了get()和set()的方法,可以在当前线程内存储和获取任何对象。
ThreadLocal使用场景
- 事务管理
在一个事务中,可能存在多个并发的线程,而每个线程都会访问同一个Connection对象。为了保证线程安全,需要每个线程都持有自己的Connection,这时候就可以使用ThreadLocal来实现。
public class ConnectionManager {
private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<>();
public static Connection getConnection() throws SQLException {
Connection conn = connectionHolder.get();
if (conn == null) {
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
connectionHolder.set(conn);
}
return conn;
}
public static void closeConnection() throws SQLException {
Connection conn = connectionHolder.get();
if (conn != null) {
conn.close();
connectionHolder.remove();
}
}
}
2.避免多个线程之间产生相互影响
在多线程场景中,如果有多个线程需要同时操作同一个变量,那么ThreadLocal就可以派上用场。例如,每个线程需要一个单独的计数器,那么使用ThreadLocal可以非常方便地实现。
public class Counter {
private static ThreadLocal<Integer> counterHolder = new ThreadLocal<Integer>() {
protected Integer initialValue() {
return 0;
}
};
public static void increase() {
counterHolder.set(counterHolder.get() + 1);
}
public static int getCount() {
return counterHolder.get();
}
}
ThreadLocal使用注意点
- 内存泄漏
在使用ThreadLocal时,需要注意内存泄漏问题。因为ThreadLocal会在每个线程内部创建一个Map,如果没有显式地调用remove()方法,就可能会造成内存泄漏。
- 并发问题
ThreadLocal本身并不是线程安全的,需要在多线程情况下进行操作时需要进行同步控制。
ThreadLocal总结
- ThreadLocal提供了一种线程封闭机制,可以实现线程内的数据隔离。
- ThreadLocal常用于需要线程安全且多线程并发访问的场景。
- 在使用ThreadLocal时需要注意内存泄漏和并发问题。
参考文章:Java并发编程之ThreadLocal
示例说明
示例1:使用ThreadLocal实现每个线程中持有自己的DateFormat对象
在Java中,SimpleDateFormat是非线程安全的,因此不能再多线程环境中直接使用。但是使用ThreadLocal可以在每个线程中持有自己的SimpleDateFormat实例,从而保证线程安全。
public class SimpleDateFormatPattern {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
Runnable runnable = new Runnable() {
@Override
public void run() {
DateFormat dateFormat = SafeSimpleDateFormat.get();
System.out.println("Thread: " + Thread.currentThread().getName() + ", DateFormat-Hashcode: " + dateFormat.hashCode());
}
};
executorService.execute(runnable);
}
executorService.shutdown();
}
}
class SafeSimpleDateFormat {
private static final ThreadLocal<DateFormat> sdfHolder = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"));
private SafeSimpleDateFormat() {
}
public static DateFormat get() {
return sdfHolder.get();
}
}
示例2:使用ThreadLocal解决DAO对象中的connection问题
在DAO数据访问对象中,要保证每个方法都能获取到Connection对象,但是这个Connection对象又需要自己手动的关闭,使用ThreadLocal将Connection对象和线程绑定,线程结束时自动关闭Connection,在线程安全的基础上提高效率。
public abstract class AbstractContext {
private static final ThreadLocal<Connection> CONTEXT_HOLDER = ThreadLocal.withInitial(() -> {
try {
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
connection.setAutoCommit(false);
return connection;
} catch (SQLException e) {
throw new RuntimeException(e);
}
});
protected Connection getConnection() {
return CONTEXT_HOLDER.get();
}
public void push() {
try {
Connection connection = getConnection();
connection.commit();
connection.close();
CONTEXT_HOLDER.remove();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void pull() {
try {
Connection connection = getConnection();
connection.rollback();
connection.close();
CONTEXT_HOLDER.remove();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java并发编程之threadLocal - Python技术站