ate void setContent(String content) {
tl.set(content);
}
public static void main(String[] args) {
MyDemo demo = new MyDemo();
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
demo.setContent(Thread.currentThread().getName() + "的数据");
System.out.println("-----------------------");
System.out.println(Thread.currentThread().getName() + "--->" + demo.getContent());
}
});
thread.setName("线程" + i);
thread.start();
}
}
}
打印结果:
?
从结果来看,这样很好的解决了多线程之间数据隔离的问题,十分方便。
1.3 ThreadLocal类与synchronized关键字
1.3.1 synchronized同步方式
? 这里可能有的朋友会觉得在上述例子中我们完全可以通过加锁来实现这个功能。我们首先来看一下用synchronized代码块实现的效果:
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(){
@Override
public 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();
}
}
}
打印结果:
?
? 从结果可以发现, 加锁确实可以解决这个问题,但是在这里我们强调的是线程数据隔离的问题,并不是多线程共享数据的问题, 在这个案例中使用synchronized关键字是不合适的。
1.3.2 ThreadLocal与synchronized的区别
? 虽然ThreadLocal模式与synchronized关键字都用于处理多线程并发访问变量的问题, 不过两者处理问题的角度和思路不同。
|
synchronized |
ThreadLocal |
原理 |
同步机制采用'以时间换空间'的方式, 只提供了一份变量,让不同的线程排队访问 |
ThreadLocal采用'以空间换时间'的方式, 为每一个线程都提供了一份变量的副本,从而实现同时访问而相不干扰 |
侧重点 |
多个线程之间访问资源的同步性 |
多线程中让每个线程之间的数据相互隔离 |
总结: 在刚刚的案例中,虽然使用ThreadLocal和synchronized都能解决问题,但是使用ThreadLocal更为合适,因为这样可以使程序拥有更高的并发性。
2. 运用场景_事务案例
? 通过以上的介绍,我们已经基本了解ThreadLocal的特点。但是它具体的应用是在哪里呢? 现在让我们一起来看一个ThreadLocal的经典运用场景: 事务。
2.1 转账案例
2.1.1 场景构建
? 这里我们先构建一个简单的转账场景: 有一个数据表account,里面有两个用户Jack和Rose,用户Jack 给用户Rose 转账。
? 案例的实现就简单的用mysql数据库,JDBC 和 C3P0 框架实现。以下是详细代码 :
? (1) 项目结构
? (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;
pu