线程封闭技术

1:线程封闭技术概述

  如果数据和对象被封闭在一个线程中,就不会产生需要同步的问题,因为不会有其他线程访问这些数据和对象。这也是最简单的实现线程安全的方法之一。就在在Java语言中并没有强制规定变量必须由锁保护,也同样没有提供强制将对象封闭在某个线程中的方式。如何实现线程封闭是程序设计要考虑的,在Java语言有一些机制可以实现线程封闭,如局部变量很显然是线程封闭的,以及ThreadLocal类,即便如此在程序设计中,依然要考虑如何确保封闭在线程的对象不会再线程中逸出。 

  谈到逸出一般有以下几种情况会出现逸出:

  • 将对象引用保存在公有的静态的public static 引用变量中,这种方式任何类和所有线程都能访问该对象。
  • 发布集合类的时候,类中存放的对象也会一道逸出来。
  • 发布内部类的时候,用包含外部类的引用,这这样子就会外部类也会逸出啦。

2:常用的线程封闭技术

2.1:Ad-hoc线程封闭

  该技术是指,维护线程封闭的职责完全由程序实现来承担,这种技术十分脆弱。

2.2:栈封闭

  栈封闭就是指通过局部变量指向heap中对象的方式,因为局部变量处于JVM stack中,而该stack是线程独有的,所以该线程独有的变量指向的对象也是线程封闭,只属于该线程。这里要再讲解以下JVM中内存区域介绍。

 

 

 

 

2.2:ThreadLocal类

认真仔细学习以下ThreadLocal类的使用方法。

2.2.1:ThreadLocal类的使用方法

 

 

 

 

 ThreadLocal是Thread内部用来存放数据的ThreadLocalMap的管理者,因为ThreadLocalMap是ThreadLocal工具类的内部静态的类,所以该类不依赖于ThreadLocal存在而存在。Thread内部有两个ThreadLocalMap引用初始化为null。这个工具类的意义就是在通过ThreadLoca类来向每个Thread内部的Map中写入以ThreadLocal实例对象的key存储value。

示例代码;

import java.util.ArrayList;
import java.util.List;

public class Main {
    private static ThreadLocal<List<String>> threadLocal = new ThreadLocal<>();

    public void setThreadLocal(List<String> value) {
        threadLocal.set(value);
    }

    public void getThreadLocal() {
        List<String> l = threadLocal.get();
        for(String s:l){
            System.out.println(Thread.currentThread().getName()+" "+s);
        }
    }

    public static void main(String[] args){
        //通过,示例一个包含ThreadLocal的对象,
        //来当问不同thread的map
        final Main test = new Main();

        new Thread(new Runnable() {
            @Override
            public void run() {
                List<String> strs = new ArrayList<String>();
                strs.add("1");
                strs.add("2");
                strs.add("3");
                test.setThreadLocal(strs);
                test.getThreadLocal();
            }
         },"t1").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                List<String> strs = new ArrayList<String>();
                strs.add("a");
                strs.add("b");
                strs.add("c");
                test.setThreadLocal(strs);
                test.getThreadLocal();
            }
        },"t2").start();
    }
}

结果:

 

 

 用法如上,通过一个ThreadLocal对象实例,来访问不同Thread类的内部map,将数据存储在线程私有的Map里!

2.2.2:ThreadLocal类源码解读

ThreadLocal类中,主要有三个使用方法,get(),set()和remove()方法。

  • T get()
    public T get() {
      //获取使用该方法的线程 Thread t
= Thread.currentThread();
      //然后再通过该线程获取其内部的ThreadLocalMap ThreadLocalMap map
= getMap(t);
      //如果map不为null,
if (map != null) {
        //则通过this对象,即当前ThreadLocal实例来获得对应的Entry。 ThreadLocalMap.Entry e
= map.getEntry(this);
        //如果没有获得null,则用当前value,创建一个result,返回。
        if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }
  • void set(T value) 
    public void set(T value) {
      //获得当前线程 Thread t
= Thread.currentThread();
      //获得当前线程的ThreadLocalMap ThreadLocalMap map
= getMap(t);
      //如果map不为null
if (map != null)
        //则通过ThreadLocalMap的set方法以this为key,写入value。
        //this就是TheadLocal的实例
map.
set(this, value); else
        
        //否则,为当前线程创建一个新的ThreadLocalMap。 createMap(t, value); }
  • void remove()
     public void remove() {
      //获取当先线程的map ThreadLocalMap m
= getMap(Thread.currentThread()); if (m != null)
        //不为null,则移除以当前ThreadLocal的key,value的键值对。
m.remove(
this); }

2.2.3:ThreadLocal类避免内存泄露

  ThreadLocalMap的Entry是WeakReference的子类,这样能保证线程中的映射表的每一个Entry可以被垃圾回收,而不至于发生内存泄露。因为ThreadLocal作为全局的Key,其生命周期很可能比一个线程要长,如果Entry是一个强引用,那么线程对象就一直持有ThreadLocal的引用,而不能被释放。随着线程越来越多,这些不能被释放的内存也就越来越多。通常再get数据后,要移除?

 

posted @ 2020-03-07 15:59  大朱123  阅读(352)  评论(0编辑  收藏  举报