线程安全一直是程序猿们关注的焦点,多线程也一直是比较让人头疼的话题,想必大家曾经也遇到过各种各种的问题,我就不再累述了。当然,解决方式也有很多,这篇博文给大家提供一种很好的解决线程安全问题的思路。
首先,我们先简单的认识一下ThreadLocal,之后是实例+解析,最后一句话总结。
1、认识一下ThreaLocal
认识ThreadLocal必须要通过api文档,不仅仅具有说服力,而且它会给你更加全面的解释。下面我我给大家从api文档上截取一张图,并标出来了七点需要重点理解的内容,实例过后的解析也是重点解释这七部分。
对于上面的内容,不理解没有关系,我们通过下面的实例加深一下理解,实例之后我会给大家一个更加深入的解释。
2、ThreaLocal封装Connection实例+解析
下面的代码只是ThreaLocal封装Connection的核心代码,对于多余的内容成功避开就好,并且有一部分代码是“dom4j解析xml文件,连接数据库”的内容,非常适合初学者,如有需要,请您移驾到此。
package com.bjpowernode.drp.util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; /** * 采用ThreadLocal封装Connection * 只要线程是活动的,没有结束,ThreadLocal是可访问的,就可以访问本线程的connection * * @author liang * */ public class ConnectionManager { //使用ThreadLocal保存Connection变量 private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>(); /** * 连接Connection * @return */ public static Connection getConnection(){ //ThreadLocal取得当前线程的connection Connection conn = connectionHolder.get(); //如果ThreadLocal没有绑定相应的Connection,创建一个新的Connection, //并将其保存到本地线程变量中。 if(conn == null){ try { JdbcConfig jdbcConfig = XmlConfigReader.getInstance().getJdbcConfig(); Class.forName(jdbcConfig.getDriverName()); conn = DriverManager.getConnection(jdbcConfig.getUrl(), jdbcConfig.getUserName(), jdbcConfig.getPassword()); //将当前线程的Connection设置到ThreadLocal connectionHolder.set(conn); } catch (ClassNotFoundException e) { e.printStackTrace(); throw new ApplicationException("系统错误,请联系系统管理员"); } catch (SQLException e) { e.printStackTrace(); throw new ApplicationException("系统错误,请联系系统管理员"); } } return conn; } /** * 关闭Connection,清除集合中的Connection */ public static void closeConnection(){ //ThreadLocal取得当前线程的connection Connection conn = connectionHolder.get(); //当前线程的connection不为空时,关闭connection. if(conn != null){ try{ conn.close(); //connection关闭之后,要从ThreadLocal的集合中清除Connection connectionHolder.remove(); }catch(SQLException e){ e.printStackTrace(); } } } }
下面的代码给大家演示了:ThreadLocal如何在同一个线程中可以共享Connection资源。
package com.bjpowernode.drp.flowcard.manager.impl; import java.sql.Connection; import java.util.Date; import com.bjpowernode.drp.flowcard.dao.FlowCardDao; import com.bjpowernode.drp.flowcard.domain.FlowCard; import com.bjpowernode.drp.flowcard.manager.FlowCardManager; import com.bjpowernode.drp.util.ApplicationException; import com.bjpowernode.drp.util.BeanFactory; import com.bjpowernode.drp.util.ConnectionManager; import com.bjpowernode.drp.util.DaoException; import com.bjpowernode.drp.util.PageModel; public class FlowCardManagerImpl implements FlowCardManager { private FlowCardDao flowCardDao; //构造函数 public FlowCardManagerImpl(){ this.flowCardDao = (FlowCardDao) BeanFactory.getInstance().getDaoObject(FlowCardDao.class); } @Override public void addFlowCard(FlowCard flowCard) throws ApplicationException { Connection conn = null; try{ //从ThreadLocal中获取线程对应的Connection conn = ConnectionManager.getConnection(); //开始事务 ConnectionManager.beginTransaction(conn); //生成流向单单号 String flowCardVouNo = flowCardDao.generateVouNo(); //添加流向单主信息 flowCardDao.addFlowCardMaster(flowCardVouNo, flowCard); //添加流向单明细信息 flowCardDao.addFlowCardDetail(flowCardVouNo, flowCard.getFlowCardDetailList()); //提交事务 ConnectionManager.commitTransaction(conn); }catch(DaoException e){ //回滚事务 ConnectionManager.rollbackTransaction(conn); throw new ApplicationException("添加流向单失败!"); }finally{ //关闭Connection并从ThreadLocal集合中清除 ConnectionManager.closeConnection(); } } }
解析:
1、该类提供了线程局部变量,它独立于变量的初始化副本
大家可能对局部变量不太理解,为什么不是成员变量或全局变量,此时就涉及到变量的作用域问题。ThreadLocal具有比局部变量更大一点的作用域,在此作用域内资源可以共享,线程是安全的。
我们还了解到ThreadLocal并不是本地线程,而是一个线程变量,它只是用来维护本地变量。针对每个线程提供自己的变量版本,避免了多线程的冲突问题,每个线程只需要维护自己的版本就好,彼此独立,不会影响到对方。
2、每个线程有自己的一个ThreadLocal,修改它并不影响其他线程
我们根据下面这张图可以看到,向ThreadLocal里面存东西就是创建了一个Map,一个线程对应一个Map集合,然后ThreadLocal把这个Map挂到当前的线程底下,一个key值对应一个value,这样Map就只属于当前线程。(如果您不理解Map的特点可以猛戳)
3、在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。
上面我们知道了变量副本的存放在了map中,当我们不在调用set,此时不在将引用指向该‘map’,而本线程退出时会执行资源回收操作,将申请的资源进行回收,其实就是将引用设置为null。这时已经不在有任何引用指向该map,故而会被垃圾回收。
3、对比ThreadLocal和synchronized同步机制
相同点:
1、ThreadLocal和线程同步机制都能解决多线程中相同变量的访问冲突问题。
不同点:
1、适用的情况不同
在同步机制中,使用同步保证同一时间只有一个线程访问,不能同时访问共享资源,否则就是出现错误。ThreadLocal则隔离了相关的资源,并在同一个线程中可以共享这个资源。彼此独立,修改不会影响到对方。
2、最终实现的效果不同
对于多线程资源共享问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。
上面博客的链接同样也是线程同步机制synchronized的实例,大家可以通过两个实例体会一下它们的异同点,再加上异同点解析,相信您对它们已经有了很深刻的认识。
4、一句话总结ThreadLocal
ThreadLocal是解决线程安全问题一个很好的思路,在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,并且程序拥有更高的并发性。
相关推荐
Java事务和ThreadLocal是两种在Java编程中至关重要的概念,它们分别用于处理多线程环境下的数据一致性问题和提供线程局部变量。 首先,我们来深入理解Java事务。在数据库操作中,事务是一系列操作的集合,这些操作...
总的来说,结合JDBC的事务管理和ThreadLocal,我们可以在多线程环境中更好地实现数据库操作,确保数据的一致性,并提高代码的可复用性和安全性。通过使用ThreadLocal,我们可以创建线程安全的变量,使得每个线程都能...
java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多...
Java中的ThreadLocal是一个非常重要的工具类,它在多线程编程中扮演着独特角色,尤其在处理线程间数据隔离和共享时。ThreadLocal不是线程本身,而是为每个线程提供一个独立的变量副本,使得每个线程都可以独立地改变...
- 同步机制:为了防止多个线程访问同一资源时产生数据不一致的问题,Java提供了`synchronized`关键字,可以锁定代码块或整个方法,确保同一时刻只有一个线程执行。 - 等待/通知机制:`wait()`, `notify()`, `...
在Java多线程编程中,`ThreadLocal`是一个非常重要的工具类,它提供了一种在每个线程内部存储线程私有实例的方法。通常情况下,当多个线程共享某个变量时,可能会引发线程安全问题。而`ThreadLocal`则提供了另一种...
从本质上说,ThreadLocal是一种存储机制,它可以在每个线程中存储一个变量的副本,这样每个线程都可以访问自己的变量副本,而不需要与其他线程共享同一个变量。这种机制可以解决多线程编程中的线程安全问题,并且...
- 不适用于跨线程通信:ThreadLocal只保证同一线程内的数据隔离,不同线程间无法共享ThreadLocal变量。 - 不是线程安全的:尽管ThreadLocal提供了线程隔离,但它本身并不保证线程安全性,如果在`set`和`get`操作之间...
Java中的ThreadLocal是一个非常重要的工具类,它在多线程编程中扮演着独特角色,用于为每个线程提供独立的变量副本。理解ThreadLocal的工作原理和使用方法对于编写高效、安全的多线程程序至关重要。 ### ...
总结来说,Java中的临界区通过`synchronized`关键字或Lock对象来保证多线程环境下的数据一致性,而ThreadLocal则通过为每个线程提供变量的独立副本,避免了共享资源的并发问题。在实际编程中,开发者需要根据具体...
- **Executor框架**:`ExecutorService`、`ThreadPoolExecutor`和`Executors`工厂类,理解它们的用法和原理,有助于优化线程资源的管理。 - **线程池参数调整**:核心线程数、最大线程数、队列容量等参数的设置对...
Java提供了各种手段来实现线程安全,如synchronized关键字、volatile变量、ThreadLocal等。 3. **并发控制**:Java提供了多种并发控制工具,包括synchronized、wait()、notify()、notifyAll()、ReentrantLock(可重...
- ThreadLocal不是线程同步机制,不能用来解决多个线程访问共享资源的问题。 - 不要将ThreadLocal用作全局变量,否则可能导致内存泄漏。 - 使用ThreadLocal时,要特别注意线程结束后的清理工作,防止内存泄漏。 - ...
总结,`ThreadLocal`是Java中用于实现线程局部变量的工具,它提供了一种简单的方式在多线程环境下隔离数据,避免了传统的同步机制。但是,使用时需注意其潜在的内存泄漏风险和对代码可读性的影响,合理地选择使用...
Java线程编程中的ThreadLocal类是一个非常重要的工具,它在多线程环境下提供了一种线程局部变量的机制。ThreadLocal并非是简单的变量,而是一种能够确保每个线程都拥有独立副本的变量容器。理解ThreadLocal的工作...
在Java中,ThreadLocal可以通过ThreadLocal类的实例来实现线程间隔离的变量访问。例如,在示例代码中,我们使用了ThreadLocal<String> currentUid = ThreadLocal.withInitial(() -> null);来定义一个ThreadLocal变量...
Java中的ThreadLocal是一种特殊类型的变量,它主要用于在多线程环境下提供线程范围内的局部变量。每个线程都拥有自己独立的ThreadLocal变量副本,互不影响。这与传统的全局变量不同,全局变量在所有线程间共享,可能...
因此,ThreadLocal非常适合在多层架构中管理数据库连接、用户认证信息等需要线程安全但又不宜在多线程间共享的资源。 数据库连接是一个经典的例子。在没有使用连接池的原始JDBC方式中,每次通过DriverManager.get...
Java中的ThreadLocal类是一种线程绑定机制,用于在多线程环境中为每个线程提供独立的变量副本,避免了线程间的数据共享带来的并发访问问题。ThreadLocal并不是一个线程对象,而是线程局部变量,即...