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://tech.souyunku.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对象的现象。