Java并发包学习ThreadLocal

ThreadLocal直译为“本地线程”,但如果你真那么认为,那就错了!其实,ThreadLocal就是一个容器,用于存放线程的局部变量。它应该叫做ThreadLocalVariable(线程局部变量)才对。

在JDK1.2开始,java.lang.ThreadLocal就诞生了,是为了解决多线程并发问题而设计的,只是设计的有些难用,所以至今没有得到广泛的使用。

ThreadLocal的示例:

1. 先看不用ThreadLocal会有什么问题。

需求:一个多线程获取序列号的程序,多线程互不干扰,对于每个线程,获取完序列号后,序列号自动加1.

public interface Sequence {
    int getNumber();
}

写一个线程类,用于获取序列号三次,每次获取后,序列号自动加1

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());
        }
    }
}

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();
    }
}

运行结果如下:

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

可以看到,由于number是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();
    }
}

这样,通过ThreadLocal封装了一个Integer类型的numberContainer静态成员变量,并且初始值是0。再看getNumber()方法,首先从numberContainer中get出当前的值,然后+1,在set回numberContainer中,然后将numberContainer中get出当前的值,并返回。

是不是很恶心,但的确很强大。确实稍微绕了一下,我们不妨把ThreadLocal看成是一个容器,这里,故意用container这个单词作为后缀来命名ThreadLocal变量。

运行结果如下:

Thread-0 => 1
Thread-0 => 2
Thread-0 => 3
Thread-2 => 1
Thread-2 => 2
Thread-2 => 3
Thread-1 => 1
Thread-1 => 2
Thread-1 => 3

这样每个线程互相独立了,同样是static变量,对于不同的线程而言,它没有被共享,而是每个线程各一份,这样就保证了线程安全。也就是说,ThreadLocal为每个线程提供了一个独立的副本。

注意:当您在一个类中使用了 static 成员变量的时候,一定要多问问自己,这个 static 成员变量需要考虑“线程安全”吗?(也就是说,多个线程需要独享自己的 static 成员变量吗?)如果需要考虑,那就请用 ThreadLocal 吧!

猜你喜欢

转载自blog.csdn.net/shijinghan1126/article/details/86500698