参考链接:

其他先不看 先学习 threadlocal

ThreadLocal

ThreadLocal 不同于普通的局部变量和全局变量,它可以理解为全局变量,但在线程维度属于局部变量。也就是说,ThreadLocal在多线程中为每一个线程开一个变量副本,不同线程的修改不会相互影响,也就从解决了某些场景下的并发问题。

应用场景

  • 变量跨层传递时,避免多次传递
  • 多线程数据隔离

下面给出具体的使用案例:

数据库连接

https://www.cnblogs.com/-beyond/p/13111015.html 这里举了个用于数据库获取的例子:

假设要实现一个事务,包含a和b步骤。没用ThreadLocal的实现如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class DBUtil {
// 返回数据库连接
public static Connection getConnectionFromPool() throws SQLException {
        return dataSource.getConnection();
}
}

public class UserService {
try{
	// 获取数据库连接
	// 关于此连接的自动提交
	// 步骤a a()
	// 步骤b b()
} catch (Exception e) {
	// 回滚此连接
}

public class UserDao {
// 定义方法a(),b()
a() {
	// 获取数据库连接
	// a的动作
}

b() {
	// 获取数据库连接
	// b的动作
}
}
}

会产生一个问题:没法保证UserServiceUserDao方法里获取的数据库连接是同一个,这样就保证步骤a和b的数据库连接是被设置为notAutiCommit,因此回滚可能失败。

一个解决方案是可以通过传参将此连接参数传递到方法中,这样就能精确使用此连接参数。但是这种做法很不优雅,维护成本也高。

如果使用ThreadLocal,一切就简单许多:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 只需要在DBUtil中将获取的连接设置为ThreadLocal,其他两个类不用改
public class DBUtil {
private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();
// 返回数据库连接
public static Connection getConnectionFromPool() throws SQLException {
        if (threadlocal.get() == null) {
	        threadLocal.set(datasource.getConnection);
        }
        return threadLocal.get();
}
}

ThreadLocal的副作用

内存泄露