ThreadLocal的介绍与运用( 二 )


public class Demo02 {private String content;public String getContent() {return content;}public void setContent(String content) {this.content = content;}public static void main(String[] args) {Demo02 demo02 = new Demo02();for (int i = 0; i < 5; i++) {Thread t = new Thread(){@Overridepublic void run() {synchronized (Demo02.class){demo02.setContent(Thread.currentThread().getName() + "的数据");System.out.println("-------------------------------------");String content = demo02.getContent();System.out.println(Thread.currentThread().getName() + "--->" + content);}}};t.setName("线程" + i);t.start();}}}打印结果:
?

ThreadLocal的介绍与运用

文章插图
? 从结果可以发现, 加锁确实可以解决这个问题 , 但是在这里我们强调的是线程数据隔离的问题,并不是多线程共享数据的问题, 在这个案例中使用synchronized关键字是不合适的 。
1.3.2 ThreadLocal与synchronized的区别? 虽然ThreadLocal模式与synchronized关键字都用于处理多线程并发访问变量的问题, 不过两者处理问题的角度和思路不同 。
synchronizedThreadLocal原理同步机制采用'以时间换空间'的方式, 只提供了一份变量,让不同的线程排队访问ThreadLocal采用'以空间换时间'的方式, 为每一个线程都提供了一份变量的副本,从而实现同时访问而相不干扰侧重点多个线程之间访问资源的同步性多线程中让每个线程之间的数据相互隔离总结: 在刚刚的案例中,虽然使用ThreadLocal和synchronized都能解决问题,但是使用ThreadLocal更为合适,因为这样可以使程序拥有更高的并发性 。2. 运用场景_事务案例? 通过以上的介绍,我们已经基本了解ThreadLocal的特点 。但是它具体的应用是在哪里呢? 现在让我们一起来看一个ThreadLocal的经典运用场景: 事务 。
2.1 转账案例2.1.1 场景构建? 这里我们先构建一个简单的转账场景: 有一个数据表account , 里面有两个用户Jack和Rose,用户Jack给用户Rose 转账 。
? 案例的实现就简单的用mysql数据库,JDBC 和 C3P0 框架实现 。以下是详细代码 :
? (1) 项目结构
ThreadLocal的介绍与运用

文章插图
? (2) 数据准备
-- 使用数据库use test;-- 创建一张账户表create table account( id int primary key auto_increment, name varchar(20), money double);-- 初始化数据insert into account values(null, 'Jack', 1000);insert into account values(null, 'Rose', 1000);? (3) C3P0配置文件和工具类
<c3p0-config><!-- 使用默认的配置读取连接池对象 --><default-config> <!--连接参数 --> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql://localhost:3306/test</property> <property name="user">root</property> <property name="password">1234</property> <!-- 连接池参数 --> <property name="initialPoolSize">5</property> <property name="maxPoolSize">10</property> <property name="checkoutTimeout">3000</property></default-config></c3p0-config>?(4) 工具类 : JdbcUtils
package com.itheima.transfer.utils;import com.mchange.v2.c3p0.ComboPooledDataSource;import java.sql.Connection;import java.sql.SQLException;public class JdbcUtils {// c3p0 数据库连接池对象属性private static final ComboPooledDataSource ds = new ComboPooledDataSource();// 获取连接public static Connection getConnection() throws SQLException {return ds.getConnection();}//释放资源public static void release(AutoCloseable... ios){for (AutoCloseable io : ios) {if(io != null){try {io.close();} catch (Exception e) {e.printStackTrace();}}}}public static void commitAndClose(Connection conn) {try {if(conn != null){//提交事务conn.commit();//释放连接conn.close();}} catch (SQLException e) {e.printStackTrace();}}public static void rollbackAndClose(Connection conn) {try {if(conn != null){//回滚事务conn.rollback();//释放连接conn.close();}} catch (SQLException e) {e.printStackTrace();}}}? (5) dao层代码 : AccountDao
package com.itheima.transfer.dao;import com.itheima.transfer.utils.JdbcUtils;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.SQLException;public class AccountDao {public void out(String outUser, int money) throws SQLException {String sql = "update account set money = money - ? where name = ?";Connection conn = JdbcUtils.getConnection();PreparedStatement pstm = conn.prepareStatement(sql);pstm.setInt(1,money);pstm.setString(2,outUser);pstm.executeUpdate();JdbcUtils.release(pstm,conn);}public void in(String inUser, int money) throws SQLException {String sql = "update account set money = money + ? where name = ?";Connection conn = JdbcUtils.getConnection();PreparedStatement pstm = conn.prepareStatement(sql);pstm.setInt(1,money);pstm.setString(2,inUser);pstm.executeUpdate();JdbcUtils.release(pstm,conn);}}

推荐阅读