Combined with threadlocal to see Spring transaction source, feel the washing of spring-like!

Source: Internet
Author: User
Tags aop

In my blog spring transaction source parsing , a key point is mentioned: Bind connection to the current thread to ensure that the database operation in this thread is using the same connection. But there is no detail on how to bind and why, and there is no connection pooling problem: How to get from the connection pool, how to return the connection to the connection pool, and so on. Then please listen to me slowly.

ThreadLocal

Before we talk about spring, let's take a look at threadlocal, which occupies a relatively important position in spring affairs, whether you are familiar with threadlocal or not, and are still listening to my monk-like words.

One thing to emphasize: threadlocal is not intended to solve the problem of shared variables, and it has nothing to do with multithreading concurrency problems.

Basic introduction

When you use threadlocal to maintain variables, Threadlocal provides a separate copy of the variable for each thread that uses the variable, so each thread can independently change its own copy without affecting the copy of the other thread, as in the following example:

 Public classthreadlocaltest{ThreadLocal<Long> longlocal =NewThreadlocal<long>(); ThreadLocal<String> stringlocal =NewThreadlocal<string>();  Public voidset () {Longlocal.set (1L);    Stringlocal.set (Thread.CurrentThread (). GetName ()); }         Public LongGetlong () {returnLonglocal.get (); }         PublicString getString () {returnStringlocal.get (); }         Public Static voidMain (string[] args)throwsinterruptedexception {FinalThreadlocaltest test =Newthreadlocaltest ();     Test.set (); //Initialize threadlocal         for(inti=0; i<10; i++) {System.out.println (test.getstring ()+ ":" + test.getlong () +i); } Thread thread1=NewThread () { Public voidrun () {test.set ();  for(inti=0; i<10; i++) {System.out.println (test.getstring ()+ ":" + test.getlong () +i);        }            };        };                Thread1.start (); Thread thread2=NewThread () { Public voidrun () {test.set ();  for(inti=0; i<10; i++) {System.out.println (test.getstring ()+ ":" + test.getlong () +i);        }            };        };    Thread2.start (); }}

Execution results are as follows

As you can see, the longlocal value of each thread is independent from the stringlocal value, and the cumulative operation of this thread does not affect the value of other threads, and really achieves the effect of thread internal isolation.

SOURCE Interpretation

Here I do not carry out threadlocal source parsing, suggest that everyone to see my reference blog, personally think that two blog can be read to threadlocal have a very deep understanding.

Make a repetitive emphasis (refer to [Java and Contract learning seven] to decrypt a paragraph in threadlocal):

Read the threadlocal source code, do not know whether we have a doubt: why like so design? If you design, how would you design? Believe that most people will have this idea, I also think: "Each threadlocal class to create a map, and then use the ID of the thread as the map key, the instance object as the map value, so that the value of the individual threads to isolate the effect" This is how the earliest threadlocal of the JDK was designed. (not sure if it is 1. ) 3) after threadlocal design in a way, that is, the current way, what advantages: 1, so the number of entry each map is smaller: Before the number of thread, now is the number of threadlocal, can improve performance, It is said that the performance of the promotion is not a little bit two (no pro-Test) 2, when the thread destroyed after the corresponding Threadlocalmap also destroyed, can reduce memory usage. 

Threadlocal in the spring transaction

The most common threadlocal usage scenarios are used to solve database connections, session management, and so on, so let's look at the application of threadlocal in spring transactions.

New Classpathxmlapplicationcontext ("Applicationcontext-jdbc.xml"= (Daoimpl) ac.getbean ("Daoimpl"); System.out.println (Daoimpl.insertuser ("yes", 25));

As long as there is a transaction configuration on the method, class, or interface of a class, spring will generate proxies for instances of that class. So Daoimpl is a reference to the proxy instance of the Daoimpl instance, not a reference to the instance of Daoimpl (the target instance), and when we invoke the method of the target instance, the method that corresponds to the proxy instance is actually called, if the target method is not @transactional (or AOP annotations , of course, does not involve AOP) modification, then the proxy method directly reflects the invocation of the target method, if the target method is @transactional decorated, then the proxy method will first perform the enhancement (for example, to determine whether the current thread exists connection, does not exist is new and bound to the current thread, etc.), The target method is then executed through reflection, and finally back to the proxy method to perform the enhancement (for example, transaction rollback or transaction commit, connection return to connection pool, etc.). Here the binding connection to the current thread on the use of threadlocal, let's look at the source code

@Overrideprotected voidDobegin (Object transaction, transactiondefinition definition) {datasourcetransactionobject txobject=(datasourcetransactionobject) transaction; Connection Con=NULL; Try {                if(Txobject.getconnectionholder () = =NULL||Txobject.getconnectionholder (). Issynchronizedwithtransaction ()) {            //get a connection from the connection poolConnection Newcon = This. Datasource.getconnection (); if(logger.isdebugenabled ()) {Logger.debug ("Acquired Connection [" + Newcon + "] for JDBC transaction"); }            //package Newcon, and assign values to Txobject, and Mark is new ConnectionholderTxobject.setconnectionholder (NewConnectionholder (Newcon),true); } txobject.getconnectionholder (). Setsynchronizedwithtransaction (true); Con=Txobject.getconnectionholder (). getconnection (); Integer Previousisolationlevel=datasourceutils.prepareconnectionfortransaction (con, definition);        Txobject.setpreviousisolationlevel (Previousisolationlevel); //Switch to Manual commit if necessary. This is very expensive in some JDBC drivers,//So we don't want to does it unnecessarily (for example if we ve explicitly//configured the connection pool to set it already).        if(Con.getautocommit ()) {Txobject.setmustrestoreautocommit (true); if(logger.isdebugenabled ()) {Logger.debug ("Switching JDBC Connection [" + con + "] to manual commit"); } con.setautocommit (false); } txobject.getconnectionholder (). Settransactionactive (true); intTimeout =determinetimeout (definition); if(Timeout! =Transactiondefinition.timeout_default)        {Txobject.getconnectionholder (). Settimeoutinseconds (timeout); }        //if the new Connectionholder, bind it to the current thread//Bind The session holder to the thread.        if(Txobject.isnewconnectionholder ()) {Transactionsynchronizationmanager.bindresource (GetDataSource ()), TXOBJ        Ect.getconnectionholder ()); }    }    Catch(Throwable ex) {if(Txobject.isnewconnectionholder ()) {datasourceutils.releaseconnection (con, This. DataSource); Txobject.setconnectionholder (NULL,false); }        Throw NewCannotcreatetransactionexception ("Could not open JDBC Connection for Transaction", ex); }}
/*** Bind The given resource for the given key to the current thread. *@paramkey The key to bind the value to (usually the resource factory) *@paramvalue The value to bind (usually the active resource object) *@throwsIllegalStateException If there is already a value bound to the thread *@seeresourcetransactionmanager#getresourcefactory ()*/ Public Static voidBindresource (Object key, Object value)throwsillegalstateexception {//key: Usually refers to the resource factory, which is the connection factory, value: usually refers to the activity of the resources, that is, the activity of the Connectionholder//unwrap a given connection pool, if necessary, otherwise returns the given connection pool as-is. Object Actualkey =transactionsynchronizationutils.unwrapresourceifnecessary (key); Assert.notnull (Value,"Value must not being null"); Map<object, object> map =Resources.get (); //Set ThreadLocal map If none found new if ThreadLocal map does not exist and set it to resources//Private static final Threadlocal<map<object, object>> resources = new Namedthreadlocal<map<object, Object>> ("Transactional resources");
//This is the threadlocal process. if(Map = =NULL) {Map=NewHashmap<object, object>(); Resources.set (map); } Object OldValue=map.put (Actualkey, value); //transparently suppress a resourceholder that is marked as void ... if(OldValueinstanceofResourceholder &&((Resourceholder) oldValue). Isvoid ()) {OldValue=NULL; } if(OldValue! =NULL) { Throw NewIllegalStateException ("Already value [" + OldValue + "] for key [" +Actualkey+ "] bound to thread [" + Thread.CurrentThread (). GetName () + "]"); } if(logger.istraceenabled ()) {Logger.trace ("Bound Value [" + Value + "] for key [" + Actualkey + "] to thread [" +Thread.CurrentThread (). GetName ()+ "]"); }}

Summarize

1, threadlocal can solve the problem, it is certainly not a shared variable (multi-threaded concurrency) problem, it just looks like concurrency; the problem of real shared variables like train tickets, movie tickets and so on threadlocal is not solved, same time, the same bus seat, Do you dare to use threadlocal to solve it?

2, each thread maintains a THREADLOCALMAP mapping table, the key of this mapping table is the threadlocal instance itself, value is the object that really needs to be stored

3, Druid Connection pool with an array to hold the Connectionholder, not I think the list,connectionholder is unbound from the thread, returned to the array connection pool ; Connectionholder is a connection package.

Questions:

Private static final Threadlocal<map<object, object>> resources = new Namedthreadlocal<map<object, Object>> ("Transactional resources");

1, why is Threadlocal<map<object, object>> instead of threadlocal<connectionholder>

2, Threadlocal<map<object, object>> in the Map of the key is why is DataSource

Hope to know the friend enlighten, comments or private messages can be, thank you!

Combined with threadlocal to see Spring transaction source, feel the washing of spring-like!

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.