java之自己做一个ThreadLocal

来源:互联网 发布:mac os x10.7.5下载 编辑:程序博客网 时间:2024/04/28 06:40

直接聊技术!

描述

ThreadLocal直译为 本地线程,但是实际意思却不是这样的,它是一个容器,用于存放本地线程的局部变量,到底为什么叫ThreadLocal,讲道理,我也不知道!


业务描述
在一个类中调用三个线程,三个线程分别进行报数1、2、3

 /** * @author:稀饭 * @time:下午8:10:59 * @filename:Sequence.java */package demo;public interface Sequence {    public int getNumber();}/** * @author:稀饭 * @time:下午8:12:24 * @filename:ClientThread.java */package demo;public class ClientThread extends Thread {    private Sequence sequence;    public ClientThread(Sequence sequence) {        // TODO Auto-generated constructor stub        this.sequence = sequence;    }    /**     * @Title: run     * @Description: TODO     */    @Override    public void run() {        // TODO Auto-generated method stub        for (int i = 0; i < 3; i++) {            System.out.println(Thread.currentThread().getName() + " ==> " + sequence.getNumber());        }    }}/** * @author:稀饭 * @time:下午8:18:27 * @filename:TestA.java */package demo;public class TestA implements Sequence {    private static int number = 0;    @Override    public int getNumber() {        // TODO Auto-generated method stub        number = number + 1;        return number;    }    public static void main(String[] args) {        Sequence sequence = new TestA();        ClientThread clientThread1 = new ClientThread(sequence);        ClientThread clientThread2 = new ClientThread(sequence);        ClientThread clientThread3 = new ClientThread(sequence);        clientThread1.start();        clientThread2.start();        clientThread3.start();    }}

运行结果如下:
Thread-0 ==> 1
Thread-2 ==> 3
Thread-1 ==> 2
Thread-2 ==> 5
Thread-0 ==> 4
Thread-2 ==> 7
Thread-1 ==> 6
Thread-0 ==> 8
Thread-1 ==> 9

**源码分析:**Thread没有按照先后顺序输出,这个可以理解,毕竟线程的启动是随机的,而为什么输出的结果不是1、2、3、1、2、3、1、2、3呢,仔细分析才发现,number是static的,是类所共享的,无法保证对不同线程来说是安全的,大家操作的都是同一个变量,当然一直在递增了。


那么如何做到各自线程递增这样的结果呢?


现在引入ThreadLocal,源码如下

/** * @author:稀饭 * @time:下午8:18:27 * @filename:TestA.java */package demo;public class TestB implements Sequence {    private static ThreadLocal<Integer> container = new ThreadLocal<Integer>() {        @Override        protected Integer initialValue() {            // TODO Auto-generated method stub            return 0;        }    };    @Override    public int getNumber() {        // TODO Auto-generated method stub        container.set(container.get() + 1);        return container.get();    }    public static void main(String[] args) {        Sequence sequence = new TestB();        ClientThread clientThread1 = new ClientThread(sequence);        ClientThread clientThread2 = new ClientThread(sequence);        ClientThread clientThread3 = new ClientThread(sequence);        clientThread1.start();        clientThread2.start();        clientThread3.start();    }}

运行结果如下:
Thread-0 ==> 1
Thread-1 ==> 1
Thread-2 ==> 1
Thread-1 ==> 2
Thread-0 ==> 2
Thread-1 ==> 3
Thread-2 ==> 2
Thread-0 ==> 3
Thread-2 ==> 3

源码解析:在TestA中引入ThreadLocal之后输出的结果变成了我上面说的那样,用了ThreadLocal之后每个线程独立了,虽然同样是static,但是线程独立了,也就是说ThreadLocal会为每一个不同的线程设置一个独立的副本。


现在才是最重要的地方,我们自己来实现一个ThreadLocal

先来分析一下ThreadLocal的api:

 public void set(T vlue) 将值放入线程局部变量中 public T get() 从线程局部变量中获取 public void remove() 从线程局部中移除值 protected T initialValue() 返回线程局部变量中的初始值

以下是我自己实现的ThreadLocal


/** * @author:稀饭 * @time:上午10:36:34 * @filename:ThreadLocalContainer.java */package demo;import java.util.Collections;import java.util.HashMap;import java.util.Map;public class ThreadLocalContainer<T> {    private T value;    //这里使用了同步Map    private Map<String, T> map = Collections.synchronizedMap(new HashMap<String, T>());    // 将值放入线程局部变量中    public void set(T vlue) {        map.put(Thread.currentThread().getName(), vlue);    }    // 从线程局部变量中获取    public T get() {        if (!map.containsKey(Thread.currentThread().getName())&&map.get(Thread.currentThread().getName()) == null) {            value = initialValue();            map.put(Thread.currentThread().getName(), value);        }        T v = map.get(Thread.currentThread().getName());        return v;    }    // 从线程局部中移除值    public void remove() {        if (map.containsKey(Thread.currentThread().getName())) {            map.remove(Thread.currentThread().getName());        }    }    // 返回线程局部变量中的初始值    protected T initialValue() {        return value;    }}

源码分析:在源码中我使用了线程安全的Map也就是同步Map,目的是为了防止出现多线程出现的不安全问题。


Note:发布的这些文章全都是自己边学边总结的,难免有纰漏,如果发现有不足的地方,希望可以指出来,一起学习咯,么么哒。
开源爱好者,相信开源的力量必将改变世界:
* osc :* https://git.oschina.net/xi_fan
github: https://github.com/wiatingpub

0 0