Java中的ThreadLocal

来源:互联网 发布:得益数据库营销案例 编辑:程序博客网 时间:2024/06/10 22:16

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

原创粉丝点击