JavaJava多线程专题

[Java多线程编程之六] 线程封闭

2019-10-21  本文已影响0人  小胡_鸭

一、什么是线程封闭?

  多线程访问共享可变数据时,线程间执行顺序的不同可能导致程序运行结果的不同,即线程安全问题;所以在多线程环境下,不涉及到共享的数据,就要通过线程封闭防止数据被共享修改,将数据封闭在各自的线程之中,这种避免同步的技术就是线程封闭

二、线程封闭的实现

1、ThreadLocal

  ThreadLocal是Java中一种特别的线程级别的变量,当线程访问时,会为每个线程创建一个变量的副本,副本之间彼此独立,互不影响,从代码上看不同线程访问的是同一个变量,但实际上不同线程访问ThreadLocal时,获取到的封装其中的数据存储在不同的内存中,这保证了线程安全,代码示例如下:

// 线程封闭示例
public class Demo7 {
    /** threadLocal变量,每个线程都有一个副本,互不干扰 */
    public static ThreadLocal<String> value = new ThreadLocal<String>();
    
    public void threadLocalTest() throws Exception {        
        value.set("这是主线程设置的123");       // 主线程设置值
        String v = value.get();
        System.out.println("线程1执行之前,主线程取到的值:" + v);
        
        new Thread(new Runnable() {
            @Override
            public void run() {
                String v = value.get();
                System.out.println("线程1取到的值:" + v);
                // 设置threadLocal
                value.set("这是线程1设置的456");
                
                v = value.get();
                System.out.println("重新设置之后,线程1取到的值:" + v);
                System.out.println("线程1执行结束");
            }
        }).start();
        
        Thread.sleep(5000L);    // 等待所有线程执行结束
        v = value.get();
        System.out.println("线程1执行之后,主线程取到的值:" + v);
    }
    
    public static void main(String[] args) throws Exception {
        new Demo7().threadLocalTest();
    }
}

【代码解析】代码中有两个线程:主线程和匿名线程(姑且将其称为线程1),主线程率先访问ThreadLocal变量并设置其包装的String对象的值为 这是主线程设置的123 打印输出设置后的效果;接着执行线程1,线程1也去访问ThreadLocal,但是它拿到的数据值是空的,因为此时JVM为线程1中的ThreadLocal包装的数据分配了一个新的内存,接着线程1设置值并打印出来;线程1执行结束后,回到主线程,打印出来的值依然是执行线程1前主线程设置的值,说明两个线程对ThreadLocal的读取和设置操作是隔离的,互不影响。

执行结果:


如何理解ThreadLocal是一种线程级别的变量?

  每个线程都会有自己的栈,正常情况下多个线程访问一个共享对象时,是在自己的栈中持有对这个对象的引用,指向堆内存中的内存对象,如图所示:



  而当线程访问的是ThreadLocal时,ThreadLocal会为每个线程单独创建一个对象,不同线程对持有的对象进行操作互不干扰,也无法通过线程发布,是一种绝对安全的线程隔离,保证线程安全,如图所示:



  从某种程度上看,ThreadLocal可以看成是线程跟数据对象映射的Map,即Map<Thread, T>,每个线程读取数据时就通过当前线程去map里面get,从这个角度看,
threadLocal.get() = map.get(Thread.currentThread())
threadLocal.set(obj) = map.put(Thread.currentThread(), obj)
2、局部变量

  局部变量被封闭在线程的栈中,而线程栈是属于线程私有的内存区域,因此其他线程无法访问,从而保证线程安全,常见的局部变量有方法中的局部变量,循环中的局部变量等。

public void fun() {
    String str = ...;
}

while (true) {
    String str = ...;
}

for (int i = 0; i < length: i++) {
    int j = ...;
}
上一篇 下一篇

猜你喜欢

热点阅读