JAVA并发-内置锁和ThreadLocal
2016-08-11 14:08
218 查看
上一篇博客讲过,当多个线程访问共享的可变变量的时候,可以使用锁来进行线程同步。那么如果线程安全性存在的3个前提条件不同时存在的话,自然就不需要考虑线程安全性了。或者说如果我们能够将某个共享变量变为局部变量,那么自然线程安全性问题就不存在了。
我们把“诸如将全局变量变为局部变量”这种将某个对象封闭在一个线程中的技术称为线程封闭,在《JAVA并发编程实践》中是这样说的,这么说有一定道理。但我还是想说说个人对锁和线程封闭的理解:
内置锁的机制是为了“使得多个线程都能够访问共享变量,而且能够留下对这个共享变量的影响”。
线程封闭的机制是为了“使得多个线程都能够使用共享变量,但不需要留下对这个共享变量的影响”。
说到底,两种机制应对的代码使用场景不同,而非是解决线程安全问题的两种方案。
线程封闭机制强调局部的概念,就是在写代码的时候,尽量使用局部变量代替全局变量(这种叫做栈封闭),如果一定要使用全局变量,而又想让多个线程之间在访问共享变量的时候互不影响,那就使用ThreadLocal<T>。ThreadLocal<T>提供了一种方式,可以让线程在操作共享变量时,复制该共享变量的一个副本到线程自己的栈空间,以后就操作这个副本空间来代替共享空间。这是一种封闭的手段,但我更加认为是一种代码场景。说个例子吧:
这是一个线程不安全的代码,输出的结果无法预测。将这段代码变为线程安全可以有几种方案,举其中两个例子来说明本文的内容:
对于上面的代码,我们使用同步机制的来实现线程安全:tc1-3这三个线程都在访问同一个num空间,并且他们都在干一件事,那就是让这个空间的数字增加,并且能够留下自己的影响(即num++)。此时,输出的结果最大值一定是num=9(具体哪个线程贡献的哪一段就不知道了)。
对于上面的代码,我们使用线程封闭来完成,tc1-3这三个线程访问共享变量在自己栈空间的一个副本,他们都在干自己的事(不是一件事),只不过在干自己的事的过程中使用到了共享变量这个载体,而且他们也不关心最终对共享变量产生了多少影响。此时,输出的结果最大值一定是num=3(每个线程在干自己的事情)。
综上,个人觉得使用锁还是线程封闭去解决线程安全问题,终究是业务逻辑的不同,或者说是代码功能的不同。我们要掌握的就是有这些个解决代码安全行的方法,然后放到具体的场景下去应用。
笔者开设了一个知乎live,详细的介绍的JAVA从入门到精通该如何学,学什么?提供给想深入学习和提高JAVA能力的同学,欢迎收听https://www.zhihu.com/lives/932192204248682496
我们把“诸如将全局变量变为局部变量”这种将某个对象封闭在一个线程中的技术称为线程封闭,在《JAVA并发编程实践》中是这样说的,这么说有一定道理。但我还是想说说个人对锁和线程封闭的理解:
内置锁的机制是为了“使得多个线程都能够访问共享变量,而且能够留下对这个共享变量的影响”。
线程封闭的机制是为了“使得多个线程都能够使用共享变量,但不需要留下对这个共享变量的影响”。
说到底,两种机制应对的代码使用场景不同,而非是解决线程安全问题的两种方案。
线程封闭机制强调局部的概念,就是在写代码的时候,尽量使用局部变量代替全局变量(这种叫做栈封闭),如果一定要使用全局变量,而又想让多个线程之间在访问共享变量的时候互不影响,那就使用ThreadLocal<T>。ThreadLocal<T>提供了一种方式,可以让线程在操作共享变量时,复制该共享变量的一个副本到线程自己的栈空间,以后就操作这个副本空间来代替共享空间。这是一种封闭的手段,但我更加认为是一种代码场景。说个例子吧:
@UnThreadSafe pulic class TestNum{ private int num=0; public int getNextNum(){ ++num; return num; } public static void main(String [] args){ TestNum tn=new TestNum(); TestClass tc1=new TestClass(tn); TestClass tc2=new TestClass(tn); TestClass tc3=new TestClass(tn); tc1.start(); tc2.start(); tc3.start(); } class TestClass extends Thread{ private TestNum tn; public TestClass(TestNum tn){ this.tn=tn; } public void run(){ for(int i=0;i<3;i++){ System.out.println("thread-"+Thread.currentThread().getName()+"-"+tn.getNextNum()); } } } }
这是一个线程不安全的代码,输出的结果无法预测。将这段代码变为线程安全可以有几种方案,举其中两个例子来说明本文的内容:
@ThreadSafe pulic class TestNum{ private int num=0; public synchronized int getNextNum(){ ++num; return num; } public static void main(String [] args){ TestNum tn=new TestNum(); TestClass tc1=new TestClass(tn); TestClass tc2=new TestClass(tn); TestClass tc3=new TestClass(tn); tc1.start(); tc2.start(); tc3.start(); } class TestClass extends Thread{ private TestNum tn; public TestClass(TestNum tn){ this.tn=tn; } public void run(){ for(int i=0;i<3;i++){ System.out.println("thread-"+Thread.currentThread().getName()+"-"+tn.getNextNum()); } } } }
对于上面的代码,我们使用同步机制的来实现线程安全:tc1-3这三个线程都在访问同一个num空间,并且他们都在干一件事,那就是让这个空间的数字增加,并且能够留下自己的影响(即num++)。此时,输出的结果最大值一定是num=9(具体哪个线程贡献的哪一段就不知道了)。
@ThreadSafe pulic class TestNum{ private ThreadLocal<Integer> num=new ThreadLocal<Integer>(){ public Integer initialValue(){ return 0; } }; public int getNextNum(){ num.set(num.get()+1); return num.get(); } public static void main(String [] args){ TestNum tn=new TestNum(); TestClass tc1=new TestClass(tn); TestClass tc2=new TestClass(tn); TestClass tc3=new TestClass(tn); tc1.start(); tc2.start(); tc3.start(); } class TestClass extends Thread{ private TestNum tn; public TestClass(TestNum tn){ this.tn=tn; } public void run(){ for(int i=0;i<3;i++){ System.out.println("thread-"+Thread.currentThread().getName()+"-"+tn.getNextNum()); } } } }
对于上面的代码,我们使用线程封闭来完成,tc1-3这三个线程访问共享变量在自己栈空间的一个副本,他们都在干自己的事(不是一件事),只不过在干自己的事的过程中使用到了共享变量这个载体,而且他们也不关心最终对共享变量产生了多少影响。此时,输出的结果最大值一定是num=3(每个线程在干自己的事情)。
综上,个人觉得使用锁还是线程封闭去解决线程安全问题,终究是业务逻辑的不同,或者说是代码功能的不同。我们要掌握的就是有这些个解决代码安全行的方法,然后放到具体的场景下去应用。
笔者开设了一个知乎live,详细的介绍的JAVA从入门到精通该如何学,学什么?提供给想深入学习和提高JAVA能力的同学,欢迎收听https://www.zhihu.com/lives/932192204248682496
相关文章推荐
- JAVA并发-内置锁和ThreadLocal
- Java并发 : ThreadLocal
- JAVA基础 (一) 并发 ThreadLocal与Synchronized 用哪一个好
- Java并发编程:深入剖析ThreadLocal
- Java并发编程:深入剖析ThreadLocal
- 转Java并发包学习七]解密ThreadLocal
- java并发编程实践 ThreadLocal
- [Java并发包学习七]解密ThreadLocal
- Java并发 ThreadLocal
- 【Java并发编程】之一:可重入内置锁
- Java并发编程--理解ThreadLocal
- JAVA基础 (一) 并发 ThreadLocal与Synchronized 用哪一个好
- 【Java并发编程实践】— ThreadLocal分析
- Java并发编程-ThreadLocal
- Java并发编程-34-生成并发随机数-ThreadLocalRandom
- Java并发 : ThreadLocal的几种误区
- 【Java并发编程实践】— ThreadLocal分析
- 一步步学习java并发编程模式之Active Object模式(五) 使用JDK的内置实现
- java并发编程---ThreadLocal
- Java并发编程--线程封闭(Ad-hoc封闭 栈封闭 ThreadLocal)