线程安全一直是程序员们关注的焦点。多线程也一直是比較让人头疼的话题,想必大家以前也遇到过各种各种的问题。我就不再累述了。当然,解决方案也有非常多,这篇博文给大家提供一种非常好的解决线程安全问题的思路。

首先。我们先简单的认识一下ThreadLocal,之后是实例+解析,最后一句话总结。

1、认识一下ThreaLocal

认识ThreadLocal必需要通过api文档,不只具有说服力,并且它会给你更加全面的解释。以下我我给大家从api文档上截取一张图,并标出来了七点需要重点理解的内容,实例过后的解析也是重点解释这七部分。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaml1cWl5dWxpYW5n/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

对于上面的内容。不理解没有关系,我们通过以下的实例加深一下理解,实例之后我会给大家一个更加深入的解释。

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封装Connection,实现同一线程共享资源的更多相关文章

  1. Java项目案例之--封装的实例

    Java项目案例之---封装的实例 有一个专业类,有一个专业对象,专业名称:计算机科学与技术,专业编号:J001,专业年限:4,对年限添加约束,如果输入小于0,则默认为0,否则显示输入的值 有一个学生 ...

  2. selenium+java项目实战---百度输入框

    最近对java+selenium+testng+maven 做一个小项目的总结,并对工作中的框架在做一些小总结,大概目录为: 1.项目实战--百度登录输入框 2.项目的二次封装 3.框架分层,及po模 ...

  3. 【java项目实战】dom4j解析xml文件,连接Oracle数据库

    简单介绍 dom4j是由dom4j.org出品的一个开源XML解析包.这句话太官方.我们还是看一下官方给出的解释.例如以下图: dom4j是一个易于使用的.开源的,用于解析XML,XPath和XSLT ...

  4. 【java项目实战】一步步教你使用MyEclipse搭建java Web项目开发环境(一)

    首先.在開始搭建MyEclipse的开发环境之前.还有三步工具的安装须要完毕,仅仅要在安装配置成功之后才干够进入以下的java Web项目开发环境的搭建. 1.安装工具 第一步,下载并安装JDK,到官 ...

  5. 【java项目实战】代理模式(Proxy Pattern),静态代理 VS 动态代理

    这篇博文,我们主要以类图和代码的形式来对照学习一下静态代理和动态代理.重点解析各自的优缺点. 定义 代理模式(Proxy Pattern)是对象的结构型模式,代理模式给某一个对象提供了一个代理对象,并 ...

  6. React Native商城项目实战04 - 封装TabNavigator.Item的创建

    1.Main.js /** * 主页面 */ import React, { Component } from 'react'; import { StyleSheet, Text, View, Im ...

  7. 【Java项目实战】——DRP之HTML总结

    在DRP的学习之中,又将之前BS的内容又一次复习了一遍,借着复习的机会将BS的各个部分再又一次总结一下.今天来总结一下HTML. 在学习BS之后就进入了权限系统的开发之中,可是仍然发现非常多代码不会不 ...

  8. java jdbc深入理解(connection与threadlocal与数据库连接池和事务实)

    1.jdbc连接数据库,就这样子 Class.forName("com.mysql.jdbc.Driver");java.sql.Connection conn = DriverM ...

  9. 云计算Docker全面项目实战(Maven+Jenkins、日志管理ELK、WordPress博客镜像)

    2013年,云计算领域从此多了一个名词“Docker”.以轻量著称,更好的去解决应用打包和部署.之前我们一直在构建Iaas,但通过Iaas去实现统一功  能还是相当复杂得,并且维护复杂.将特殊性封装到 ...

随机推荐

  1. String,创建对象问题

    String str=new String("aaa"); 这行代码究竟创建了几个String对象呢?答案是2个,而不是3个.由于new String("aaa" ...

  2. Redis 字符串结构和常用命令

    字符串是 Redis 最基本的数据结构,它将以一个键 和一个值 储存在 Redis 内部. 字符串的一些基本命令 命令 说明 备注 set key value 设置键值对 最常用的写入命令 get k ...

  3. HangFire的定时任务和Quartz.NET总结(三)Quartz 配置

    在一个Net Core需求中,需要在每天的凌晨三点去抓取两个电商仓库的剩余的每个料号的数量来写会自己的表中, 用到了HangFire的定时任务 这篇文章讲的很详细记录下   文章2  这篇更简单 Qu ...

  4. sql server 存储过程(事务,带参数声明,数据库瘦身)

    CREATE PROCEDURE procedureName (@var1 as varchar(50),@var2 as varchar(50)) --建立未发临时表 AS begin tran - ...

  5. 【转】Linux rpm 命令参数使用详解[介绍和应用]

    RPM是RedHat Package Manager(RedHat软件包管理工具)类似Windows里面的“添加/删除程序” rpm 执行安装包二进制包(Binary)以及源代码包(Source)两种 ...

  6. kipmi0进程单核CPU100%的解决办法

    top查看服务器进程,发现有个kipmi0的进程竟然CPU的单核占用高达100%,而且居高不下. 于是上网搜了搜大家的说法了给出的链接,大概意思是一个固件问题,可以通过修改文件来解决. 专业的解释地址 ...

  7. 物理cpu与逻辑cpu概述

    物理cpu与逻辑cpu概述(本博客属于转载部分内容:主要学习目的用于大数据平台Hadoop之yarn资源调度的配置) 一.yarn资源调度器中主要的资源分类       1.memory(内存) 2. ...

  8. loadrunner报错总结

    1.报错   没有缓存空间可用   TCP超时释放时间?是解决刚才那个报错的? 解决方法如下  修改TcpTimedWaitDelay值为1和MaxUserPort值为65534.最后,重启!  完美 ...

  9. 在oracle中将某个字段的数据作为列名的查询

    原表结构 查询语句: select sno,sname,sum(语文) 语文,sum(数学) 数学,sum(英语) 英语 from (select sno,sname,decode(subjiect, ...

  10. vue中访问数据接口的配置

    业务API接口地址: http://localhost:3816/api/ 前端UI浏览地址:http://127.0.0.1:8080/#/home 由于同源策略的问题: 需要配置代理: 在开发环境 ...