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

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项目

收藏 (0)
评论列表
正在载入评论列表...
我是有底线的
为您推荐
    暂时没有数据