ThreadLocal变量,用于数据共享

本文介绍了ThreadLocal变量的使用,通过实例展示了如何利用ThreadLocal在不同线程间保存和获取特定数据,以解决并发场景下多个线程共享连接问题。通过设置和获取ThreadLocal值,确保每个线程拥有独立的连接,避免了数据冲突。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值