ThreadLocal的由来

个人理解,请大家擦亮眼睛,有错必究,否则误人子弟!

 

首先要知道,线程安全,通俗来讲就是:一个线程调用此变量不会影响另一个线程

 

静态变量:线程非安全(因为静态变量位于方法区,所有对象共享内存,一旦静态变量被修改,其他对象均受影响,故线程非安全)

成员变量:非单例线程安全

                      单例模式线程非安全(因为成员变量为对象私有,存放在堆中,调用该对象的所有线程共享此变量)

局部变量:线程安全(每个线程执行时将会把局部变量放在各自栈帧的工作内存中,线程间不共享,故不存在线程安全问题。)

 

为什么引入ThreadLocal

ThreadLocal是JDK5.0以后才出现的

ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。


在BaseDao中protectedConnection conn;即数据库连接是非单例的(数据库参数等的初始化是单例),一个线程中,每次new一个dao,然后dao.xxx方法里会有this.openConnection每次打开的都是新的connection,但是connection资源是非常有限的,所以就需要把一个线程中的connection共享给所有的dao


想到设计成protectedstaticConnection conn;达到了节省connection资源的目的,但是由于静态变量是线程不安全的,多线程情况下会共享,可能会发生某个线程还在进行操作,而另一个线程就将connection关闭或者提交,这绝对不可以。


于是又想到用synchronized锁住,但是又带来一个问题一个线程在使用connect进行数据库操作的时候,其他线程只有等待,执行效率会很低。


至此,我们需要达成两个目的:

1.   线程只打开一个connection资源(线程中的一切业务都共享connection)

2.   每个线程打开的connection不互相影响,各操作各的

即把connection这个变量本地化给这个线程,每个线程都操作本地的connection


鉴于以上情景,ThreadLocal就诞生了,ThreadLocal在每个线程中对该变量会创建一个副本,即每个线程内部都会有一个该变量,线程之间互不影响。

 

举例说明:

事物:添加班级和学生在同一事物,保证班级添加成功后再添加班级,否则学生没有外键约束出错。

 

传统方案:默认每个dao每次openConnection的时候都会打开一个新的connection,除非在事物中,通过传递的方式进行共享。

传统的jdbc再事物中需要把stuDao开启事物(开启事物中就openConnection)

然后把connection传递给classDao,即classDao.setConn(stuDao.getConn());然后classDao和stuDao这两个业务就在一条流水线上,共享同一个connection连接。

 

ThreadLocal:默认所有dao共享一个connection,不管在不在事物中,每次openConnection的时候是去ThreadLocal中取,而不是打开新的。

有了ThreadLocal以后,就可以把需要共享的connection放到ThreadLocal中(底层是Map),然后dao需要用的时候去取就可以

 

另外别把连接池和它们搞混,连接池是存放connection资源的池子,有了连接池,线程openConnection的时候就不用真正的去打开了,而是直接从池子中取出一个,用完放回即可

 

传统方案无ThreadLocal:

Ui:

    StuJdbcBiz biz = new StuJdbcBiz();

    biz.addStudentAndClass();

Biz:

public void addStudentAndClass()throws Exception{

       StuJdbcDao stuDao = new StuJdbcDao();

       ClassJdbcDao classDao = new ClassJdbcDao();

       try {

           stuDao.beginTransaction();//开启事物,打开connection

           classDao.setConn(stuDao.getConn());//传递connection

          classDao.addClass();//添加班级

          stuDao.addStudent();//添加学生

          stuDao.commit();

       } catch (Exceptione) {

          stuDao.rollback();

           e.printStackTrace();

       }

    }

开启事物

    public void beginTransaction()throws Exception {

       this.openConnection();//打开数据库连接

       if (this.conn != null){

           this.conn.setAutoCommit(false);

       }

    }

打开数据库连接

    public void openConnection()throws Exception {

       if (this.conn == null || this.conn.isClosed()){

          DbInfo dbinfo = DbInfo.instance();

          Class.forName(dbinfo.getDbDriver());         

conn =DriverManager.getConnection(dbinfo.getDbURL(),dbinfo.getUsername(),dbinfo.getPassword());

       } else {

           return;//已经打开的话直接跳出

       }

    }

Dao:

添加班级:

public void addClass()throws Exception{

       String sql = "insertintotclass values(?,?)";

       System.out.println(this.getConn().hashCode());

       this.openConnection();//conn不为空,直接return

       System.out.println(this.getConn().hashCode());

       PreparedStatement ps = this.conn.prepareStatement(sql);

       ps.setString(1, "java999");

       ps.setString(2, "c999");

       ps.executeUpdate();

       ps.close();

    }

添加学生:

    public void addStudent()throws Exception{

       String sql = "insertintotstudent values(?,?,?,?)";

       this.openConnection();//connection不为空,直接return

       PreparedStatement ps = this.conn.prepareStatement(sql);

       ps.setString(1, "s999");

       ps.setString(2, null);

       ps.setString(3, "llw999");

       ps.setInt(4, 199);

       ps.executeUpdate();

       ps.close();

    }

 

Hibernate中ThreadLocal方案:

Ui:

    StuBiz biz = new StuBiz();

    biz.addStudentAndClass();

Biz:

    public void addStudentAndClass()throws Exception{

       StuDao stuDao = new StuDao();

       ClassDao classDao = new ClassDao();

       try {

           stuDao.beginTransaction();//开启事物,打开connection

          classDao.addClass();//添加班级

          stuDao.addStudent();//添加学生

          stuDao.commit();

       } catch (Exceptione) {

          stuDao.rollback();

          e.printStackTrace();

       }

    }

打开事物

    public void beginTransaction()throws Exception{

       this.openConnection();//打开数据库连接

       transaction=this.session.beginTransaction();

//底层还是setAutoComment()false

    }

打开数据库连接

    public void openConnection()throws Exception{

       session=HibernateSessionFactory.getSession();//得到session

    }

从ThreadLocal中取得session

    public static Session getSession() throwsHibernateException{

        Session session =(Session) threadLocal.get();

//先从threadLocal中得到该线程session变量副本

 

       if (session == null ||!session.isOpen()) {

//isOpen()检查session是否打开,未打开返回true

           if (sessionFactory == null){//检查工厂是否创建

             rebuildSessionFactory();

           }

session = (sessionFactory != null)? sessionFactory.openSession(): null;//真正的打开session

           threadLocal.set(session);//session变量放入threadLocal

       }

        return session;

}

Dao

添加班级

    public void addClass()throws Exception{

       Tclass tclass = new Tclass();

       tclass.setCno("c111");

       tclass.setCname("java111");    

       this.openConnection();

       this.session.save(tclass);

    }

添加学生

    public void addStudent()throws Exception{

       Tstudent stu = new Tstudent();

       stu.setSno("s111");

       stu.setSname("llw111"); 

       this.openConnection();

       this.session.save(stu); 

    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值