ThreadLocal其实,它就是一个容器,用于存放线程的局部变量。为每个线程保留一份变量副本。
我们来看下面的代码:
数据库连接工具
package com.xfl.concurrent;
import java.sql.Connection;
import java.sql.DriverManager;
/**
* Created by XFL
* time on 2017/5/16 23:43
* description:
*/
public class DBUtil {
// 数据库配置
private static final String driver = "com.mysql.jdbc.Driver";
private static final String url = "jdbc:mysql://localhost:3306/shop";
private static final String username = "XFL";
private static final String password = "********";
// 定义一个数据库连接
private static Connection conn = null;
// 获取连接
public static Connection getConnection() {
try {
Class.forName(driver);
conn = DriverManager.getConnection(url, username, password);
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
// 关闭连接
public static void closeConnection() {
try {
if (conn != null) {
conn.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
测试线程
package com.xfl.concurrent;
import java.sql.Connection;
import java.sql.SQLException;
/**
* Created by XFL
* time on 2017/5/16 23:50
* description:
*/
public class ClientThread extends Thread {
private int wait;
private String threadName;
public ClientThread(int wait, String threadName) {
this.wait = wait;
this.threadName = threadName;
}
@Override
public void run() {
Connection connection = DBUtil.getConnection();
System.out.println("正在执行的线程: " + threadName);
try {
//模仿耗时操作任务
Thread.sleep(wait);
} catch (InterruptedException e) {
System.out.println(e);
}
try {
//主要是为了验证数据库连接是否关闭,可以起到模仿数据库操作的效果
System.out.println("线程:" + threadName + " 的数据库连接是否关闭:" + connection.isClosed());
} catch (SQLException e) {
System.out.println(e);
}
DBUtil.closeConnection();
}
}
测试代码
package com.xfl.concurrent;
/**
* Created by XFL
* time on 2017/5/16 23:34
* description:ThreadLocal线程的局部变量
* https://my.oschina.net/huangyong/blog/159489
*/
public class ThreadLocalTest {
public static void main(String[] args) {
//让线程1多等待一会,目的是为了让线程2和线程1拿到同一个数据库连接(同一个对象)
Thread thread1 = new ClientThread(1500, "thread1");
thread1.start();
Thread thread2 = new ClientThread(500, "thread2");
thread2.start();
}
}
运行结果:
我们看到线程2会把线程1的数据库连接关闭,原因很简单因为线程1和线程2共用了一个数据库连接,因为我们在DBUtil中定义的Connection是static,修改方法有很多中比如将Connection定义在方法内部,每次都去new一个,当然关闭的方法也要做修改,最好的做法是使用ThreadLocal,为每个线程保留一份Connection的副本,不同的线程不共用一个连接,就不会出现上面的问题。
修改后的DBUtil
package com.xfl.concurrent;
import java.sql.Connection;
import java.sql.DriverManager;
/**
* Created by XFL
* time on 2017/5/16 23:43
* description:
*/
public class DBUtil {
// 数据库配置
private static final String driver = "com.mysql.jdbc.Driver";
private static final String url = "jdbc:mysql://localhost:3306/shop";
private static final String username = "XFL";
private static final String password = "********";
// 定义一个数据库连接
private static Connection conn = null;
// 定义一个用于放置数据库连接的局部线程变量(使每个线程都拥有自己的连接)
private static ThreadLocal<Connection> connContainer = new ThreadLocal<Connection>();
// 获取连接
public static Connection getConnection() {
//从ThreadLocal中获取Connection
Connection conn = connContainer.get();
try {
Class.forName(driver);
conn = DriverManager.getConnection(url, username, password);
} catch (Exception e) {
e.printStackTrace();
}finally {
//将 connContainer放入到ThreadLocal中
connContainer.set(conn);
}
return conn;
}
// 关闭连接
public static void closeConnection() {
//获取Connection
Connection conn = connContainer.get();
try {
if (conn != null) {
conn.close();
}
} catch (Exception e) {
e.printStackTrace();
}finally {
//移除Connection
connContainer.remove();
}
}
}
运行结果:
我们可以看到线程1和线程2之间不会相互影响了。
参考资料:https://my.oschina.net/huangyong/blog/159725