java.lang.ThreadLocal 该类提供了线程局部(thread-local) 变量,用于在当前线程中共享数据。ThreadLocal工具 类底层就是相当于一个Map,key存放的当前线程,value存放需要共享的数据。
/*
java.lang.ThreadLocal<T>类:该类提供了线程局部 (thread-local) 变量。
ThreadLocal的底层原理: 使用一个Map集合,对Map进行了封装
Map<当前线程Thread, Object> map = new HashMap<当前线程Thread,Object>();
当前线程Thread:static Thread currentThread()
在使用方法的时候,省略了Map集合的key,只使用了Map集合的vlaue,key默认就是用的当前正在执行的线程
程序在main方法中执行:main线程
程序在run方法中执行:新创建的线程
ThreadLocal构造方法:
ThreadLocal() 创建一个线程本地变量。
ThreadLocal的成员方法:
void set(T value) 往ThreadLocal中添加数据
此方法相当于Map集合的put方法
put(Thread.currentThread(),Value);
T get() 通过当前线程,获取当前线程保存的值
此方法相当于Map集合的get方法
value = get(Thread.currentThread());
void remove() 通过当前线程,移除ThreadLocal中保存的健值
此方法相当于Map集合的remove方法
remove(Thread.currentThread())
线程局部(自己) (thread-local) 变量。
哪个线程往ThreadLocal中存储的数据,只有哪个线程能使用,其他线程不能使用
mian线程存储的数据,只有main线程能使用
Thread-0线程存储的数据,只有Thread-0线程能使用
...
*/
package com.itheima.demo03.ThreadLocal;
public class Demo01ThreadLocal {
public static void main(String[] args) {
//创建ThreadLocal对象,泛型使用String
ThreadLocal<String> tl = new ThreadLocal<>();
//使用set方法往ThreadLocal中添加数据
//底层 map.put(main线程,"main-->添加的数据")
tl.set(Thread.currentThread().getName()+"-->添加的数据");
//使用get方法获取ThreadLocal中保存的数据
//底层 map.get(main线程)
String s = tl.get(); // 值为 main-->添加的数据 键为 main 即当前线程
System.out.println(s);//main-->添加的数据
new Thread(new Runnable() {
@Override
public void run() {
//使用get方法获取ThreadLocal中保存的数据
//底层 map.get(Thread-0线程)
System.out.println(tl.get());//null 并没有存数据, 但是键是知道的 ,即当前线程
//使用set方法往ThreadLocal中添加数据
//底层 map.put(Thread-0线程,"main-->添加的数据")
tl.set(Thread.currentThread().getName()+"-->添加的数据");
System.out.println(tl.get());//Thread-0添加的数据
}
}).start(); // 新开线程
}
}
//mian线程存储的数据,只有main线程能使用
//Thread-0线程存储的数据,只有Thread-0线程能使用
ThreadLocal<String> tl = new ThreadLocal<>();字符串作为保存的内容。
Map<当前线程Thread, Object> map = new HashMap<当前线程Thread,Object>();
其中键为当前线程,值为保存的数据。
通过set保存数据作为值。其中键为当前线程。get取出当前线程保存的数据。
执行结果:
main-->添加的数据
null
Thread-0-->添加的数据
Process finished with exit code 0
执行结果中为null是因为未设置值,设置后为Thread-0-->添加的数据
/*
使用ThreadLocal存储Connection
好处:
一个线程存储的Connection,多次获取使用的是同一个
多线程并发的使用ThreadLocal存取数据,互相之间互不影响
*/
package com.itheima.demo03.ThreadLocal;
import com.itheima.demo02.utils.C3P0UtilsXML;
import java.sql.Connection;
/*
使用ThreadLocal存储Connection
好处:
一个线程存储的Connection,多次获取使用的是同一个
多线程并发的使用ThreadLocal存取数据,互相之间互不影响
*/
public class Demo02ThreadLocal {
public static void main(String[] args) {
//创建ThreadLocal对象,泛型使用Connection
ThreadLocal<Connection> tl = new ThreadLocal<>();
//使用连接池获取Connection
Connection conn1 = C3P0UtilsXML.getConnection();
//使用set方法往ThreadLocal中添加Connection
tl.set(conn1);
//使用get方法获取ThreadLocal中存储的Connection
Connection conn2 = tl.get();
System.out.println("---------------main方法中-->main线程--------------------");
System.out.println(conn1);//@2f8dad04
System.out.println(conn2);//@2f8dad04
System.out.println(conn1==conn2);//true
System.out.println(tl.get());//@2f8dad04
System.out.println(tl.get());//@2f8dad04
System.out.println(tl.get());//@2f8dad04
System.out.println(tl.get());//@2f8dad04
System.out.println(tl.get());//@2f8dad04
new Thread(new Runnable() {
@Override
public void run() {
//使用set方法往ThreadLocal中添加Connection
tl.set(C3P0UtilsXML.getConnection());
//使用get方法获取ThreadLocal中存储的Connection
System.out.println("---------------run方法中-->Thread-x线程--------------------");
System.out.println(tl.get());//@394bad33
System.out.println(tl.get());//@394bad33
System.out.println(tl.get());//@394bad33
System.out.println(tl.get());//@394bad33
System.out.println(tl.get());//@394bad33
System.out.println(tl.get());//@394bad33
System.out.println(tl.get());//@394bad33
System.out.println(tl.get());//@394bad33
}
}).start();
}
}
执行结果:
5月 30, 2019 8:29:35 下午 com.mchange.v2.log.MLog <clinit>
信息: MLog clients using java 1.4+ standard logging.
5月 30, 2019 8:29:36 下午 com.mchange.v2.c3p0.C3P0Registry banner
信息: Initializing c3p0-0.9.1.2 [built 21-May-2007 15:04:56; debug? true; trace: 10]
5月 30, 2019 8:29:36 下午 com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource getPoolManager
信息: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 2000, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> 1hge74ia32f2la01u1qys4|69b794e2, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 1hge74ia32f2la01u1qys4|69b794e2, idleConnectionTestPeriod -> 0, initialPoolSize -> 5, jdbcUrl -> jdbc:mysql://localhost:3306/day05, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 1000, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 10, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> null, properties -> {password=******, user=******}, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ]
---------------main方法中-->main线程--------------------
com.mchange.v2.c3p0.impl.NewProxyConnection@67080771
com.mchange.v2.c3p0.impl.NewProxyConnection@67080771
true
com.mchange.v2.c3p0.impl.NewProxyConnection@67080771
com.mchange.v2.c3p0.impl.NewProxyConnection@67080771
com.mchange.v2.c3p0.impl.NewProxyConnection@67080771
com.mchange.v2.c3p0.impl.NewProxyConnection@67080771
com.mchange.v2.c3p0.impl.NewProxyConnection@67080771
---------------run方法中-->Thread-x线程--------------------
com.mchange.v2.c3p0.impl.NewProxyConnection@b3eddb0
com.mchange.v2.c3p0.impl.NewProxyConnection@b3eddb0
com.mchange.v2.c3p0.impl.NewProxyConnection@b3eddb0
com.mchange.v2.c3p0.impl.NewProxyConnection@b3eddb0
com.mchange.v2.c3p0.impl.NewProxyConnection@b3eddb0
com.mchange.v2.c3p0.impl.NewProxyConnection@b3eddb0
com.mchange.v2.c3p0.impl.NewProxyConnection@b3eddb0
com.mchange.v2.c3p0.impl.NewProxyConnection@b3eddb0
Process finished with exit code 0
解决获取conn,得到的不是同一个conn的问题。
具体例子见:
D:\IdeaProjects\itcast\EE331\day18_ThreadLocal
private static ComboPooledDataSource dataSource = new ComboPooledDataSource();
private static ThreadLocal<Connection> tl = new ThreadLocal<>();
public static Connection getConnection(){
// 从ThreadLocal中获取connection
Connection conn = tl.get(); //并没有设置所以为null
if(conn==null){ // 如果再次连接,同一个线程则不会再走这一步,不再取conn
try {
conn = dataSource.getConnection();
//存储
tl.set(conn);
} catch (SQLException e) {
e.printStackTrace();
}
}
return conn; // 同一个线程中获得将是同一个conn
}
连接方式通过localThread。一个线程对应一个连接conn。
dao层不再通过传入的参数获得conn,
C3P0UtilsXMLTL.getConnection()直接获得。
//创建AccountSerivce对象
AccountService service = new AccountService(); //业务
//调用转账方法,接收转账结果
boolean b = service.transferAccount("jack", "rose", 1000); // 数据库操作 转账成功返回true
//对结果进行判断,给用户展示结果
if (b){
System.out.println("jacktorose转账成功!");
}else{
System.out.println("jacktorose转账失败!");
}
// 两个线程不同的conn不会得到同一个conn
new Thread(new Runnable() {
@Override
public void run() {
//创建AccountSerivce对象
AccountService2 service = new AccountService2(); //业务
//调用转账方法,接收转账结果
boolean b = service.transferAccount("jack", "tom", 1000); // 数据库操作 转账成功返回true
//对结果进行判断,给用户展示结果
if (b){
System.out.println("jacktotom转账成功!");
}else{
System.out.println("jacktotom转账失败!");
}
}
}).start();
两个线程访问,不会对同一个conn操作。
最总要的是同一个线程中得到的是同一个conn。