ThreadLocal理解
针对ThreadLocal理解之前,我们首先看官方是如何定义的:
This class provides thread-local variables.
也就是说:线程本地变量,线程独有的变量,作用域为当前线程。
对于我来说,通俗的解释就是:每个线程都有独立的变量,互不干涉。
ThreadLocal举例理解
首先我们思考一下,在什么情况下,我们会在多线程中用到每个线程的变量都是独立的并且互不干涉的?
在网上我们看到很多案例都是讲在多线程的情况下,数据库的连接、关闭进行举例说明的。在此基础上我们来了解一下此时的需求是什么?为什么要这么干?
情景如下:
刚开始的数据库连接都是通过JDBC进行连接、关闭的。
单线程情况下:整个数据的导入都是一个连接、一个关闭,不存在浪费资源的情况,但是导入的数据会很慢。
多线程情况下:此时我们采用多线程导入一批数据,数据库的连接、关闭要保证线程安全的,不然会导致以下几种情况:
1、创建多个数据库连接
2、数据库关闭的可能是上一个正在提交的数据库连接
若是将数据库的连接、关闭操作,放在线程内部操作,当然保证了线程的安全性,但是频繁的进行连接、关闭操作,有损性能,不能充分的利用资源。
那么此时我们的需求就是,可以将每个线程的变量或者对象只用于当前线程中,且和别的线程互不干扰。由此引出:ThreadLocal类
参考示例:
public class Jdbc {
private Connection connect = null;
public static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();
public Connection getConnect() {
return connect;
}
public void setConnect(Connection connect) {
this.connect = connect;
}
public Connection getThreadLocal() {
return this.threadLocal.get();
}
public void setThreadLocal(Connection connect) {
this.threadLocal.set(connect);
}
}
具体使用时:
public class MyRunableThreadLocalJdbc implements Runnable {
private Jdbc jdbc;
public MyRunableThreadLocalJdbc() {
}
public MyRunableThreadLocalJdbc(Jdbc jdbc) {
this.jdbc = jdbc;
}
@Override
public void run() {
if (jdbc.getThreadLocal() == null){
jdbc.setThreadLocal(this.jdbc.getConnect());
}
Connection connection = null;
try {
connection = jdbc.getThreadLocal();
// 进行一些增删改查操作即可
}catch (Exception e){
e.printStackTrace();
}finally {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
只需要在多线程使用时,将Jdbc对象的Connection连接传入即可,确保了每个Connection连接都是在每个线程中创建了一个副本,使用副本保证了每个线程的变量或者对象,都是独立的。
以上这个看不出来效果,我们比如设置一个票的数量的值为:10,并且在多线程中要求每个线程都购买10张票。
代码如下:
Tickets tickets = new Tickets();
tickets.setTickets(10);
MyRunableThreadLocal myRunable1 = new MyRunableThreadLocal(tickets);
Thread thread3 = new Thread(myRunable1, "窗口一");
MyRunableThreadLocal myRunable2 = new MyRunableThreadLocal(tickets);
Thread thread4 = new Thread(myRunable2, "窗口二");
thread3.start();
thread4.start();
thread3.interrupt();
thread4.interrupt();
public class Tickets {
private int tickets;
// 操作的是具体的值(必须用static修饰)
public static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>();
public int getTickets() {
return tickets;
}
public void setTickets(int tickets) {
this.tickets = tickets;
}
public Integer getThreadLocal() {
return this.threadLocal.get();
}
public void setThreadLocal(Integer tickets) {
this.threadLocal.set(tickets);
}
}
public class MyRunableThreadLocal implements Runnable {
private Tickets tickets;
public MyRunableThreadLocal() {
}
public MyRunableThreadLocal(Tickets ticket) {
this.tickets = ticket;
}
@Override
public void run() {
if (tickets.getThreadLocal() == null){
tickets.setThreadLocal(this.tickets.getTickets());
}
while (true){
if (tickets.getThreadLocal() > 0)
try {
System.out.println(Thread.currentThread().getName() +" 出售了第 " + tickets.getThreadLocal() + " 张票");
tickets.setThreadLocal(tickets.getThreadLocal()-1);
}catch (Exception e){
e.printStackTrace();
}
}
}
}
明显的可以看出俩个窗口各卖了10张票,确保了每个线程的对象或者变量都是独立的,互不影响的:
ThreadLocal使用场景
(1) 目标变量只与当前线程有关,每个线程需要有该值的备份
(2) 目标变量在线程执行过程中多次使用,导致需要在每个用到的方法都要作为参数传递,ThreadLocal提供了一种从当前线程取变量的途径