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为真实的线程私有变量。

posted @ 2015-09-16 13:21  平林新袖  阅读(208)  评论(0编辑  收藏  举报