Java多线程编程中ThreadLocal类的用法及深入

本文详细介绍了Java中的ThreadLocal类,解释了其作为线程局部变量的作用,通过一个序列号生成器的例子展示了如何解决线程安全问题。ThreadLocal通过为每个线程提供独立的副本,实现了线程间的变量隔离,从而保证了线程安全。文章还讨论了ThreadLocal的API用法,并提供了一个自定义的ThreadLocal实现。最后,通过一个数据库事务控制的案例,说明了ThreadLocal在实际开发中的应用,特别是在处理多线程环境下数据库连接的问题。
摘要由CSDN通过智能技术生成

ThreadLocal,直译为“线程本地”或“本地线程”,如果你真的这么认为,那就错了!其实,它就是一个容器,用于存放线程的局部变量,我认为应该叫做
ThreadLocalVariable(线程局部变量)才对,真不理解为什么当初 Sun 公司的工程师这样命名。

早在 JDK 1.2 的时代,java.lang.ThreadLocal
就诞生了,它是为了解决多线程并发问题而设计的,只不过设计得有些难用,所以至今没有得到广泛使用。其实它还是挺有用的,不相信的话,我们一起来看看这个例子吧。

一个序列号生成器的程序,可能同时会有多个线程并发访问它,要保证每个线程得到的序列号都是自增的,而不能相互干扰。

先定义一个接口:


    public interface Sequence {
   
    
      int getNumber();
    }
    
    

每次调用 getNumber() 方法可获取一个序列号,下次再调用时,序列号会自增。

再做一个线程类:


    public class ClientThread extends Thread {
   
    
      private Sequence sequence;
    
      public ClientThread(Sequence sequence) {
   
        this.sequence = sequence;
      }
    
      @Override
      public void run() {
   
        for (int i = 0; i < 3; i++) {
   
          System.out.println(Thread.currentThread().getName() + " => " + sequence.getNumber());
        }
      }
    
    
    
    }
    
    

在线程中连续输出三次线程名与其对应的序列号。

我们先不用 ThreadLocal,来做一个实现类吧。


    public class SequenceA implements Sequence {
   
    
      private static int number = 0;
    
      public int getNumber() {
   
        number = number + 1;
        return number;
      }
    
      public static void main(String[] args) {
   
        Sequence sequence = new SequenceA();
    
        ClientThread thread1 = new ClientThread(sequence);
        ClientThread thread2 = new ClientThread(sequence);
        ClientThread thread3 = new ClientThread(sequence);
    
        thread1.start();
        thread2.start();
        thread3.start();
      }
    }
    

序列号初始值是0,在 main() 方法中模拟了三个线程,运行后结果如下:


    Thread-0 => 1
    Thread-0 => 2
    Thread-0 => 3
    Thread-2 => 4
    Thread-2 => 5
    Thread-2 => 6
    Thread-1 => 7
    Thread-1 => 8
    Thread-1 => 9

由于线程启动顺序是随机的,所以并不是0、1、2这样的顺序,这个好理解。为什么当 Thread-0 输出了1、2、3之后,而 Thread-2
却输出了4、5、6呢?线程之间竟然共享了 static 变量!这就是所谓的“非线程安全”问题了。

那么如何来保证“线程安全”呢?对应于这个案例,就是说不同的线程可拥有自己的 static 变量,如何实现呢?下面看看另外一个实现吧。


    public class SequenceB implements Sequence {
   
    
      private static ThreadLocal<Integer> numberContainer = new ThreadLocal<Integer>() {
   
        @Override
        protected Integer initialValue() {
   
          return 0;
        }
      };
    
      public int getNumber() {
   
        numberContainer.set(numberContainer.get() + 1);
        return numberContainer.get();
      }
    
      public static void main(String[] args) {
   
        Sequence sequence = new SequenceB();
    
        ClientThread thread1 = new ClientThread(sequence);
        ClientThread thread2 = new ClientThread(sequence);
        ClientThread thread3 = new ClientThread(sequence);
    
        thread1.start();
        thread2.start();
        thread3.start();
      }<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值