关于threadlocal的理解

来源:互联网 发布:数据库表设计 编辑:程序博客网 时间:2024/05/21 07:49

最近在哪里接触到了threadlocal,但是发现自己对threadlocal的理解很少,基本不知道是做什么的。所以,处于一种学习的目的,找了很多介绍threadlocal的文章。


看了很多的博客文章,大多都介绍了threadlocal的概念。以下给出我看了这么多文章的对其的理解,threadlocal可以翻译成“线程局部变量”,是用来存储线程局部变量的地方,可以保证线程安全问题,其内部实现是ThreadLocal为每个使用该变量的线程分配一个独立的变量副本。还有很多描述,和同步进行对比的,说什么高效的解决了线程安全,巴拉巴拉的。。。类似这篇文章http://www.iteye.com/topic/1123824,其实讲的还蛮好的,但还是不能解决我的疑惑。


不知道大家理解到这里有没有些疑惑,但我是已经很疑惑了。请看上面描述的,说的是为每个独立线程分配了一个变量副本,请看清楚,是变量,而不是实例。在我的理解中,一个变量只是一个引用,变量的副本就是意味着多个引用,但是指向的实例是同一个!!多个线程相互之间改变实例的状态,怎么可能会没有线程安全问题呢???excuse me??


为了验证我的猜想,我最终写下了以下测试代码。

public class TestThreadLocal {    @Data    static class Modal1 {        private String str;        private Integer integer;    }    @Test    public void test1() throws InterruptedException {        Modal1 modal1 = new Modal1();        modal1.setStr("123");        modal1.setInteger(123);        ThreadLocal<Modal1> modal1ThreadLocal = new ThreadLocal<>();        Thread testClient = new TestClient(modal1, modal1ThreadLocal);        Thread testClient1 = new TestClient1(modal1, modal1ThreadLocal);        testClient.start();        testClient1.start();        testClient1.join();//        System.out.println(modal1);    }    private static class TestClient extends Thread    {        private Modal1 modal1;        private ThreadLocal threadLocal;        public TestClient(Modal1 modal1, ThreadLocal threadLocal) {            this.modal1 = modal1;            this.threadLocal = threadLocal;        }        public void run(){            try {                Thread.sleep(3000L);            } catch (InterruptedException e) {                e.printStackTrace();            }            threadLocal.set(modal1);            modal1.setStr("456");            modal1.setInteger(456);            //④每个线程打出3个序列值            for (int i = 0; i < 3; i++) {                System.out.println("thread[" + Thread.currentThread().getName() +                        "] modal1[" + threadLocal.get() + "]");            }        }    }    private static class TestClient1 extends Thread    {        private Modal1 modal1;        private ThreadLocal threadLocal;        public TestClient1(Modal1 modal1, ThreadLocal threadLocal) {            this.modal1 = modal1;            this.threadLocal = threadLocal;        }        public void run(){            threadLocal.set(modal1);            //④每个线程打出3个序列值            for (int i = 0; i < 3; i++) {                System.out.println("thread[" + Thread.currentThread().getName() +                        "] modal1[" + threadLocal.get() + "]");            }            try {                Thread.sleep(5000L);            } catch (InterruptedException e) {                e.printStackTrace();            }            System.out.println(threadLocal.get());        }    }}

果然结果就是我想的,不会保证线程安全。

thread[Thread-1] modal1[TestThreadLocal.Modal1(str=123, integer=123)]thread[Thread-1] modal1[TestThreadLocal.Modal1(str=123, integer=123)]thread[Thread-1] modal1[TestThreadLocal.Modal1(str=123, integer=123)]thread[Thread-0] modal1[TestThreadLocal.Modal1(str=456, integer=456)]thread[Thread-0] modal1[TestThreadLocal.Modal1(str=456, integer=456)]thread[Thread-0] modal1[TestThreadLocal.Modal1(str=456, integer=456)]

所以各位大神们,难道是我的理解那么有问题吗?为了弄清楚这个问题,我又拿着我的疑惑去请教公司内的一个大神。果然经过了大神的一番指导,我终于弄清楚了我的困惑。


其实好多的博客拿同步和threadlocal比是不对的,关于我上面的问题,多个线程访问同一个实例产生的线程安全问题threadlocal是解决不了的,而同步是能解决这个线程安全问题的(忽略性能)。threadlocal把变量本地化解决的是线程之间变量相互引用所产生的线程安全问题。所以,两者在这个方面是没有可比性的!!!
而且上面我的例子对于threadlocal的使用也是错误的。一般会类似这样的使用:

a = new Object();threadlocal.set(a);

这样使用的话确实是从某一方面解决了线程安全问题,但也绝对不应该拿来和同步来比较。其实辩证的来思考这件事情,线程局部变量就是为了线程所经过的所有方法都能获得这个变量,用一种很笨的方式也可以实现相同的效果,就是所有线程经过的方法参数都加上这个变量,也是可以实现目的的,只不过这种方式太笨而已。


综上,threadlocal的使用还是限定在有限的场景里,例如数据库链接,但只要明白它是做什么的,解决什么问题,有时候还是一个利器呢。