Java线程学习12--案例--ThreadLocal线程本地变量的详解

 1.ThreadLocal是什么
           ThreadLocal正如他的中文翻译 线程本地变量所说,顾名思义,就是每个线程自己的本地变量。简单的来说,就是在多线程的程序中,我们一般会申请共享变量。给所有线程使用。但是很多情况下,每个线程会对共享变量进行改变。比如,一个String 类型的static变量,线程一将String 赋值为 “线程一”,然后在跑其他的逻辑的时候,第二个线程将String 又改成了“线程二”,那么就会出个问题,线程一跑到后面在对该String进行使用的时候,就发现值已经被改变了。

           ThreadLocal的价值就体现了出来。就是在每个线程里面对变量单独记录保存。这样,线程二对String的改变就是对自己内部改变,不会影响别的线程中的String值。

           针对这个,写个代码进行了一个测试,方便理解。

package zxy.maven.product;
 
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
//
public class ThreadTest {
    // threadLocal Test
    public static ThreadLocal threadLocal = new ThreadLocal();
    
    public static String baseString ="";
    
    
    public static ExecutorService  executorService=Executors.newFixedThreadPool(4);
 
    
    private static void setTLocal(String value) {
        threadLocal.set(value);
    }
 
    private static String getTlocal() {
        return "ThreadLocal保存:"+Thread.currentThread().getName()+" : "+ threadLocal.get();
    }
    
    private static void setBLocal(String value) {
        baseString=value;
    }
 
    private static String getBlocal() {
        return "Baseocal保存:   "+Thread.currentThread().getName()+" : "+baseString;
    }
    
    
    
    public static void main(String[] args) throws InterruptedException {
 
        Thread thread=new Thread(new Runnable() {
            public void run() {
                setTLocal("子线程一保存变量");
                try {
                    //睡眠一秒,模拟在处理某些程序
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(getTlocal());
            }
        });
        
        Thread thread2=new Thread(new Runnable() {
            public void run() {
                setTLocal("子线程二保存变量");
                System.out.println(getTlocal());
            }
        });
        
        executorService.execute(thread);
        executorService.execute(thread2);
        
        setTLocal("主线程保存变量");
        System.out.println(getTlocal());
        
        Thread.sleep(1000);
        
        
        
        Thread thread3=new Thread(new Runnable() {
            public void run() {
                setBLocal("子线程一保存变量");
                try {
                    //睡眠一秒,模拟在处理某些程序
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.err.println(getBlocal());
            }
        });
        
        Thread thread4=new Thread(new Runnable() {
            public void run() {
                setBLocal("子线程二保存变量");
                System.err.println(getBlocal());
            }
        });
        
        executorService.execute(thread3);
        executorService.execute(thread4);
        
        setBLocal("主线程保存变量");
        System.err.println(getBlocal());    
    }    
}

 


           看到结果,ThreadLocal保存的变量没有因为其他原因改变。但是Basecal的变了,因为当执行的时候,主线程执行完了输出,线程一将String 修改为“子线程一保存变量”,进入睡眠,线程二进行修改,改String 为子线程二,这时候输出的时候,就变成了两个子线程二了

      如此就体现了ThreadLocal的价值,就是保证线程之间的变量处理互不影响。

                    

2.ThreadLocal是怎么实现的线程本地
       从代码上来看看怎么实现了这个功能吧。
       ThreadLocal内部主要的几个方法就是   public T get(), public void set(T value), public void remove(),分别是获取本地变量,保存本地变量和删除变量

        从get方法开始看
         public T get():
                    

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

               很简单,就是先获取当前正在执行的线程,获取当前执行的线程的ThreadLocalMap(每个Thread都有一个名字叫threadLocals  类型为ThreadLocal.ThreadLocalMap的变量)。如果,当前Thread的ThreadLocalMap不为空,即已经通过set或者initialValue初始化过,则直接返回有关对象类型。
               如果当前线程的ThreadLocalMap为空,则处理setInitialValue方法。

private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

           该方法的作用是对ThreadLocalMap进行初始化。首先如果有重写initialValue方法,则将重写的初始化对象给T,然后在尝试查一下当前线程ThreadLocalMap有没有值,有值则将初始化的值也set进去,没有值得话就创建一个新的map,把当前线程和T当做参数传递给createMap。
               

  void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }


    createMap很简单,就是把当前线程的ThreadLocalMap用初始化的值保存一下。
               

  public void set(T value)
              
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

              看完了setInitialValue方法,其实这个就太简单了,只是把初始化的那个值换成了set 的value值,保存在了ThreadLocalMap里面,其他没有变化。

 public void remove()
               
     public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

remove方法也非常简单,就是获取当前线程的ThreadLocalMap,如果不为空的话,把他清除掉。
看完了这些方法,基本上ThreadLocal也就完全明白了,其实还是比较简单的一个实现的东西。
 

你可能感兴趣的:(02-05-多线程)