ThreadLocal

ThreaedLocal本质上是一个存数据的map集合,元素的键默认为当前线程,值是通过set()方法存储的数据。

1、向线程中取数据和存放数据:

package pers.zhb.thread;
import java.util.Random;
public class ThreadDemo {
    private static ThreadLocal<Integer> x=new ThreadLocal<Integer>();
    public static void main(String[] args){
        for(int i=0;i<2;i++){//创建线程
            new Thread(new Runnable(){
                public void run(){
                    int data=new Random().nextInt();//随机数
                    System.out.println(Thread.currentThread().getName()+"data"+data);
                    x.set(data);//数据放入线程
                    new A().get();
                    new B().get();
                }
            }).start();
        }
    }
    static class A{
        public void get(){
            int data=x.get();
            System.out.println("A"+Thread.currentThread().getName()+"data"+data);
        }
    }
    static class B {
        public void get() {
            int data =x.get();
            System.out.println("B" + Thread.currentThread().getName() + "data" + data);
        }
    }
}

运行结果:

 开辟的两个线程,分别存放不同的数据,但是在取出数据的时候用两个类中的方法去取数据,数据的值是相同的。也就是说同一个线程中的数据是可以共享的。

2、ThreadLocal的应用:

在使用事务进行银行转账的项目中(https://www.cnblogs.com/zhai1997/p/11703092.html),虽然实现了转账的功能,但是为了保证对事务的处理操纵的始终是同一个对象,Connection对象的创建写在了Service层,并可以向dao层传递该参数,把Connection对象的创建写在Service层是不合理的。因此,可以创建一个数据库工具类将对Connection对象的创建引入到工具类里面,dao层和Service层的Connection对象的获取都是通过工具类里里面的方法获得的。

而工具类里面的Connection对象是从ThreadLocal中获得的,如果ThreadLocal为空,则重新从连接池中获取Connection对象,即通过ThreadLocal的数据共享功能实现了Connection对象的一致。

代码实现:

jsp:

<form action="${pageContext.request.contextPath}/transferservlet" method="post">
    转出账户:<input type="text" name="out"><br>
    转入账户:<input type="text" name="in"><br>
    转账金额:<input type="text" name="money"><br>
    <input type="submit" value="确认转账"><br>
</form>

web层:

Servlet:

protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
        response.setContentType("text/html;charset=utf-8");
        String inName=request.getParameter("in");//从表单获得的数据都为字符串类型
        String outName=request.getParameter("out");
        String stringmoney=request.getParameter("money");
        double money=Double.parseDouble(stringmoney);//字符串类型的money变为double
        TransferService transferService=new TransferService();
        boolean result=transferService.transfer(outName,inName,money);
        if(result){
            response.getWriter().write("成功");
        }else{
            response.getWriter().write("失败");
        }
    }

从表单获得数据。

Service层:

 public boolean transfer(String outName,String inName,double money) {
        boolean result=true;
        try {
            ThreaddbUtils.startTransaction();//将Connection对象隐藏在了ThreaddbUtils中
            TransferDao dao = new TransferDao();
            dao.out(outName, money);//将同一个con对象以参数的形式传递到dao层
            //int num=8/0;//故意制造异常,使得转账过程意外终止
            dao.in(inName, money);
        }catch (Exception e){
            result=false;
            try {
                    ThreaddbUtils.rollback();//调用工具类里面的rollback()方法
            }catch (Exception e1){
                e1.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            ThreaddbUtils.commit();//调用工具类里面的commit()方法
        }
        return true;
    }

对转账事务进行处理,无异常则直接提交,有异常则回滚。

dao层:

public class TransferDao {
    public void out(String outName, double money) {
        try {
            Connection con= ThreaddbUtils.getConnection();
            QueryRunner qr = new QueryRunner();
            String sql = "UPDATE transfer SET money=money-? WHERE username=? ";
            Object[] update = {money, outName};
            qr.update(con, sql, update);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    public void in(String inName, double money) {
        try {
            Connection con= ThreaddbUtils.getConnection();
            QueryRunner qr = new QueryRunner();
            String sql = "UPDATE transfer SET money=money+? WHERE username=? ";
            Object[] update = {money, inName};
            qr.update(con, sql, update);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

从工具类里面的ThreadLocal方法中获得Connection对象,保证操作的是同一个Connection对象。

工具类:

public class ThreaddbUtils {
    private static ComboPooledDataSource dataSource = new ComboPooledDataSource("zhai");
    private static ThreadLocal<Connection> threadLocal=new ThreadLocal<Connection>();//只存储元素的值,键已经默认了
    public static void startTransaction(){
        Connection con=getConnection();
        try{
            con.setAutoCommit(false);
        }catch (SQLException e){
            e.printStackTrace();
        }
    }
    public static Connection getConnection() {
        try {
            Connection con=threadLocal.get();//先尝试从集合中获取Connection的对象
            if(con==null){//Map集合中没有connection对象,则从连接池中获取一个,并将该对象存储到map集合中
             con= dataSource.getConnection();
             threadLocal.set(con);//存储Connection对象
            }
            return con;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
    public static void rollback(){
        try{
            getConnection().rollback();
        }catch (SQLException e){
            e.printStackTrace();
        }
    }
    public static void commit(){
        try{
Connection con=getConnection();
con.commit();
threadLocal.remove();
con.close();
}catch (SQLException e){ e.printStackTrace(); } } }

将Connection对象存放在ThreadLocal对象中,只要是调用getConnection()方法获得的连接都是同一个Connection对象,并将回滚和提交写在此工具类内部,保证了Service层不会再出现创建Connection对象的现象。

原文地址:https://www.cnblogs.com/zhai1997/p/11708943.html