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对象的现象。