msql笔记七——ThreadLocal保证客户端同时拿到的是同一个连接,数据库多事务的处理

时间:2022-03-22 20:39:27

1、简介:JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序,ThreadLocal并不是一个Thread,而是Thread的局部变量。

2、jkd介绍:该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。
每个线程都保持对其线程局部变量副本的隐式引用,只要线程是活动的并且 ThreadLocal 实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。

3、个人理解:ThreadLocal相当于一个Map容器,里面的key是Thread,value是Object对象,对于每一个key都有对应的对象,而map维护的就是一个currenctThread对象(当前线程),可以在当前线程放入和设置Object对象,如果是在同一线程中,拿到的对象是同一个。下面我自己模拟了一个MyThreadLocal对象,其原理就是如此

package cn.hncu.threadLocalDemo;

import java.util.HashMap;

public class MyThreadLocal {
private HashMap<Thread, Object> map=new HashMap<Thread, Object>();//实质上是一个map对象
public Object get(){
Thread nowThread=Thread.currentThread();//以当前线程作为key为准,若是相同的线程,值obj对象不变
Object obj =map.get(nowThread);
return obj;

}
public void set(Object obj){
Thread nowThread=Thread.currentThread();
map.put(nowThread, obj);//吧当前线程的对象放入map中,下次拿到保证是同一个线程
}
public void remove(){
Thread nowThread=Thread.currentThread();
map.remove(nowThread);
}
}

THreadLocal使用代码:

package cn.hncu.threadLocalDemo;

import java.util.Random;

import org.junit.Test;

public class ThreadLocalDemo {
static ThreadLocal<Object> tl=new ThreadLocal<Object>();
public static Object getvalue(){
Object obj1=tl.get();
if(obj1==null){
Random r=new Random();
obj1=r.nextInt(500);
tl.set(obj1);
}
return obj1;
}
@Test
public void test(){
Object obj1=getvalue();
Object obj2=getvalue();
System.out.println(obj1+" "+obj2);
System.out.println(obj1==obj2);
Object obj3=new A().aa();
System.out.println(obj2+" "+obj3);
System.out.println(obj2==obj3);//true,相同线程
System.out.println("____________");
final Object obj4=new B().bb();
System.out.println(obj3+" "+obj4);
System.out.println(obj4==obj3);//true,相同线程
System.out.println("____________");
new Thread(){

@Override
public void run() {
A a = new A();
Object obj5 = a.aa();
System.out.println("obj5:"+obj5);
B b = new B();
Object obj6 = b.bb();
System.out.println("obj6:"+obj6);
System.out.println(obj5==obj6);//true,相同线程
System.out.println(obj4==obj6);//false,相同线程
}

}.start();

}

}
class A{
public Object aa(){
Object obj3=ThreadLocalDemo.getvalue();
return obj3;
}
}

class B{
public Object bb(){
Object obj4=ThreadLocalDemo.getvalue();
return obj4;
}
}

ThreadLocal解决多线程程序的并发问题,解决数据库事务的处理

我们在数据库dao层中,难免会遇到不同的事务,但是不同事物之间怎么解决并发问题呢。怎么保证一个用户拿到的是同一个Connection对象呢?那么必须使用ThreadLocal来解决了,下面是我改进了connection工具类

package cn.hncu.pubs;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

public class ConUtil {
private static ThreadLocal<Connection> tl=new ThreadLocal<Connection>(); //引入ThreadLocal来操纵同一个con对象,防止事物提交不一致的情况
private static List<Connection> list=new ArrayList<Connection>();//con连接池
private static final String FILE_NAME="jdbc.properities";
private static final int NUM=3;
static{
Properties p=new Properties();
try {
p.load(ConUtil.class.getClassLoader().getResourceAsStream(FILE_NAME));//用配置文件加载
String user=p.getProperty("username");
String url=p.getProperty("url");
String password=p.getProperty("password");
String driver=p.getProperty("driver");
Class.forName(driver);
for(int i=0;i<NUM;i++){
final Connection con=DriverManager.getConnection(url, user, password);
Object objCon=Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Connection.class}, new InvocationHandler() {

@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if(method.getName().equals("close")){
synchronized (ConUtil.class) {
list.add((Connection) proxy);
tl.set(null);//设为空,不能少,防止不同用户拿到同一个con
ConUtil.class.notify();
}
return null;
}
return method.invoke(con, args);
}
});
list.add((Connection)objCon);
}
} catch(Exception e) {
e.printStackTrace();
}
}
synchronized public static Connection getCon(){
Connection con=tl.get();//从ThreadLocal中拿到一个con
if(con==null){
if(list.size()<=0){
try {
ConUtil.class.wait();
// tl.set(getCon());//不能放在这里,因为第一次拿不到
} catch (InterruptedException e) {
e.printStackTrace();
}
}
con=list.remove(0);
tl.set(con);//吧con放入ThreadLocal中
}
return con;
}
public static void main(String[] args) {
System.out.println(getCon());
}
}

详细使用,请参考mysql笔记8——MVC项目