zoukankan      html  css  js  c++  java
  • ThreadLocal学习

    一、简介

      ThreadLocal的引入,在多线程情况下,实现线程变量的共享,但是又不相互影响,也就是所说的线程隔离。

      下面是非线程安全的方法实现

     1 package com.volshell.threads;
     2 
     3 import java.util.Date;
     4 import java.util.concurrent.TimeUnit;
     5 
     6 public class UnsafeTask implements Runnable {
     7     private Date startDate;
     8 
     9     @Override
    10     public void run() {
    11         // TODO Auto-generated method stub
    12         startDate = new Date();
    13         System.out.println("Start thread :" + Thread.currentThread().getId()
    14                 + ": " + startDate);
    15 
    16         try {
    17             TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10));
    18         } catch (InterruptedException e) {
    19             // TODO Auto-generated catch block
    20             e.printStackTrace();
    21         }
    22 
    23         System.out.println("Thread finished :" + Thread.currentThread().getId()
    24                 + "--" + startDate);
    25     }
    26 
    27 }

      测试代码:

    package com.volshell.threads.test;
    
    import java.util.concurrent.TimeUnit;
    
    import com.volshell.threads.UnsafeTask;
    
    public class UnsafeTaskTest {
        public static void main(String[] args) throws InterruptedException {
            UnsafeTask task = new UnsafeTask();
    
            for (int i = 0; i < 10; i++) {
                Thread t = new Thread(task);
                t.start();
    
                TimeUnit.SECONDS.sleep(2);
            }
        }
    }

    按照一般思路理解每个线程结束的时间和开始的时间是不相同的,但是有些线程有相同的结束时间!!

      下面是通过使用线程局部变量加以控制的,ThreadLocal

    package com.volshell.threads;
    
    import java.util.Date;
    import java.util.concurrent.TimeUnit;
    
    public class SafeTask implements Runnable {
        private static ThreadLocal<Date> startDate = new ThreadLocal<Date>() {
            protected Date initialValue() {
                return new Date();
            };
        };
    
        @Override
        public void run() {
            // TODO Auto-generated method stub
        
            System.out.println("Start thread :" + Thread.currentThread().getId()
                    + ": " + startDate.get());
    
            try {
                TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10));
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    
            System.out.println("----------Thread finished :" + Thread.currentThread().getId()
                    + "--" + startDate.get());
        }
    
    }

    这样,每一个线程的起始、结束时间就完全不一样了。

    二、深入源码

      ThreadLocal类提供了thread-local variables ,通过set/get方法实现对变量的初始化。在第一次调用set的时候完成初始化。

      get方法

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

      其实在其内部维护一个Map -- ThreadLocalMap,其内部同样适用Entry来实现,每一个线程用一个Entry存储。

            static class Entry extends WeakReference<ThreadLocal> {
                /** The value associated with this ThreadLocal. */
                Object value;
    
                Entry(ThreadLocal k, Object v) {
                    super(k);
                    value = v;
                }
            }

      不过他的key为ThreadLocal对象。

      回到get方法,通过当前线程获取ThreadLocalMap对象, ThreadLocalMap.Entry e = map.getEntry(this); 获取当前线程的Entry,然后就可以得到value.

      set方法与之类似,

    三、总结

      每一个线程对应着一个ThreadLocalMap,Map中存储着Entry,而Entry中的key为ThreadLocal对象,value为真实的线程私有变量。

  • 相关阅读:
    剑指 Offer 25. 合并两个排序的链表
    53. 最大子序和 动态规划
    121. 买卖股票的最佳时机
    20. 有效的括号
    centos7 常用操作
    树莓派
    golang 学习笔记
    并发 线程 进程
    连接内网问题
    Lamp 高并发优化
  • 原文地址:https://www.cnblogs.com/plxx/p/4813054.html
Copyright © 2011-2022 走看看