Emmachen / SapUi5Test

1 stars 1 forks source link

Thread local #49

Open ghost opened 7 years ago

ghost commented 7 years ago

ThreadLocal线程局部变量

在刚开始接触ThreadLocal,很难理解这个线程局部变量的使用场景。当现在回过头去看,ThreadLocal是一种多线程间并发访问变量的解决方案。与synchronized等加锁的方式不同,ThreadLocal完全不提供锁,而使用了以空间换时间的手段,为每个线程提供变量的独立副本,以保障线程安全,因此它不是一种数据共享的解决方案

ThreadLocal是解决线程安全问题一个很好的思路,ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本,由于Key值不可重复,每一个“线程对象”对应线程的“变量副本”,而到达了线程安全。 特别值得注意的地方,从性能上说,ThreadLocal并不具有绝对的优势,在并发量不是很高时,也行加锁的性能会更好。但作为一套与锁完全无关的线程安全解决方案,在高并发量或者所竞争激烈的场合,使用ThreadLocal可以在一定程度上减少锁竞争。 下面是一个ThreadLocal的简单使用:

package threadLocalTest;

import threadLocalTest.TestNum.TestClient;

class TestNum {

    // 通过匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值
    // multiple thread can share with this value, but each thread has its dedicated value
    private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>() {
        public Integer initialValue() {
            return 0;
        }
    }; // 获取下一个序列值

    public int getNextNum() {

        seqNum.set(seqNum.get() + 1);
        return seqNum.get();
    }

    static class TestClient extends Thread {
        private TestNum sn;

        public TestClient(TestNum sn) {
            this.sn = sn;
        }

        public void run() {
            for (int i = 0; i < 3; i++) {
                // 每个线程打出3个序列值
                System.out.println("thread[" + Thread.currentThread().getName()
                        + "] --> sn[" + sn.getNextNum() + "]");
            }
        }
    }
}

public class LocalTest{
    public static void main(String[] args) {
        TestNum sn = new TestNum(); // 3个线程共享sn,各自产生序列号
        TestClient t1 = new TestClient(sn);
        TestClient t2 = new TestClient(sn);
        TestClient t3 = new TestClient(sn);
        t1.start();
        t2.start();
        t3.start();
    }
}

输出结果:

thread[Thread-0] –> sn[1]thread[Thread-1] –> sn[1]thread[Thread-2] –> sn[1]thread[Thread-1] –> sn[2]thread[Thread-0] –> sn[2]thread[Thread-1] –> sn[3]thread[Thread-2] –> sn[2]thread[Thread-0] –> sn[3]thread[Thread-2] –> sn[3] 输出的结果信息可以发现每个线程所产生的序号虽然都共享同一个TestNum实例,但它们并没有发生相互干扰的情况,而是各自产生独立的序列号,这是因为ThreadLocal为每一个线程提供了单独的副本。