上一篇博客讲过,当多个线程访问共享的可变变量的时候,可以使用锁来进行线程同步。那么如果线程安全性存在的3个前提条件不同时存在的话,自然就不需要考虑线程安全性了。或者说如果我们能够将某个共享变量变为局部变量,那么自然线程安全性问题就不存在了。 
我们把“诸如将全局变量变为局部变量”这种将某个对象封闭在一个线程中的技术称为线程封闭,在《JAVA并发编程实践》中是这样说的,这么说有一定道理。但我还是想说说个人对锁和线程封闭的理解: 
内置锁的机制是为了“使得多个线程都能够访问共享变量,而且能够留下对这个共享变量的影响”。 
线程封闭的机制是为了“使得多个线程都能够使用共享变量,但不需要留下对这个共享变量的影响”。
 
说到底,两种机制应对的代码使用场景不同,而非是解决线程安全问题的两种方案。 
线程封闭机制强调局部的概念,就是在写代码的时候,尽量使用局部变量代替全局变量(这种叫做栈封闭),如果一定要使用全局变量,而又想让多个线程之间在访问共享变量的时候互不影响,那就使用ThreadLocal<T>。ThreadLocal<T>提供了一种方式,可以让线程在操作共享变量时,复制该共享变量的一个副本到线程自己的栈空间,以后就操作这个副本空间来代替共享空间。这是一种封闭的手段,但我更加认为是一种代码场景。说个例子吧:

  1. @UnThreadSafe
  2. pulic class TestNum{
  3. private int num=0;
  4. public int getNextNum(){
  5. ++num;
  6. return num;
  7. }
  8. public static void main(String [] args){
  9. TestNum tn=new TestNum();
  10. TestClass tc1=new TestClass(tn);
  11. TestClass tc2=new TestClass(tn);
  12. TestClass tc3=new TestClass(tn);
  13. tc1.start();
  14. tc2.start();
  15. tc3.start();
  16. }
  17. class TestClass extends Thread{
  18. private TestNum tn;
  19. public TestClass(TestNum tn){
  20. this.tn=tn;
  21. }
  22. public void run(){
  23. for(int i=0;i<3;i++){
  24. System.out.println("thread-"+Thread.currentThread().getName()+"-"+tn.getNextNum());
  25. }
  26. }
  27. }
  28. }

这是一个线程不安全的代码,输出的结果无法预测。将这段代码变为线程安全可以有几种方案,举其中两个例子来说明本文的内容:

  1. @ThreadSafe
  2. pulic class TestNum{
  3. private int num=0;
  4. public synchronized int getNextNum(){
  5. ++num;
  6. return num;
  7. }
  8. public static void main(String [] args){
  9. TestNum tn=new TestNum();
  10. TestClass tc1=new TestClass(tn);
  11. TestClass tc2=new TestClass(tn);
  12. TestClass tc3=new TestClass(tn);
  13. tc1.start();
  14. tc2.start();
  15. tc3.start();
  16. }
  17. class TestClass extends Thread{
  18. private TestNum tn;
  19. public TestClass(TestNum tn){
  20. this.tn=tn;
  21. }
  22. public void run(){
  23. for(int i=0;i<3;i++){
  24. System.out.println("thread-"+Thread.currentThread().getName()+"-"+tn.getNextNum());
  25. }
  26. }
  27. }
  28. }

对于上面的代码,我们使用同步机制的来实现线程安全:tc1-3这三个线程都在访问同一个num空间,并且他们都在干一件事,那就是让这个空间的数字增加,并且能够留下自己的影响(即num++)。此时,输出的结果最大值一定是num=9(具体哪个线程贡献的哪一段就不知道了)。

  1. @ThreadSafe
  2. pulic class TestNum{
  3. private ThreadLocal<Integer> num=new ThreadLocal<Integer>(){
  4. public Integer initialValue(){
  5. return 0;
  6. }
  7. };
  8. public  int getNextNum(){
  9. num.set(num.get()+1);
  10. return num.get();
  11. }
  12. public static void main(String [] args){
  13. TestNum tn=new TestNum();
  14. TestClass tc1=new TestClass(tn);
  15. TestClass tc2=new TestClass(tn);
  16. TestClass tc3=new TestClass(tn);
  17. tc1.start();
  18. tc2.start();
  19. tc3.start();
  20. }
  21. class TestClass extends Thread{
  22. private TestNum tn;
  23. public TestClass(TestNum tn){
  24. this.tn=tn;
  25. }
  26. public void run(){
  27. for(int i=0;i<3;i++){
  28. System.out.println("thread-"+Thread.currentThread().getName()+"-"+tn.getNextNum());
  29. }
  30. }
  31. }
  32. }

对于上面的代码,我们使用线程封闭来完成,tc1-3这三个线程访问共享变量在自己栈空间的一个副本,他们都在干自己的事(不是一件事),只不过在干自己的事的过程中使用到了共享变量这个载体,而且他们也不关心最终对共享变量产生了多少影响。此时,输出的结果最大值一定是num=3(每个线程在干自己的事情)。

综上,个人觉得使用锁还是线程封闭去解决线程安全问题,终究是业务逻辑的不同,或者说是代码功能的不同。我们要掌握的就是有这些个解决代码安全行的方法,然后放到具体的场景下去应用。

最新文章

  1. 【UE4游戏开发】安装UE4时报SU-PQR1603错误的解决方法
  2. h.SSL协议栈整体分解
  3. 对Java中字符串的进一步理解
  4. Professional Android Application Development
  5. C# 天气预报
  6. oc-19-成员变量修饰符
  7. 运用Autoconf和Automake生成Makefile的学习之路
  8. struts2日常
  9. 读 Working with forms 一些心得
  10. HEVC码率控制浅析——HM代码阅读之二
  11. Java_常遇问题(一)
  12. 【iCore4 双核心板_uC/OS-II】例程二:任务的建立与删除
  13. unity3d脚本语言中的引用类型
  14. shell脚本和python脚本实现批量ping IP测试
  15. Exp2_固件程序设计 20165226_20165310_20165315
  16. 文件编码检测.ZC
  17. 查看linux系统配置(centos/redhat)
  18. Hadoop HBase概念学习系列之行、行键(十一)
  19. TThread 线程的例子
  20. 2017-2018 ACM-ICPC Northern Eurasia (Northeastern European Regional) Contest (NEERC 17) 日常训练

热门文章

  1. 【BZOJ4176】Lucas的数论-杜教筛
  2. Linux 操作基础(一) -- Shell 命令格式和元字符
  3. Tomcat跨域资源共享
  4. 洛谷P4994 终于结束的起点
  5. [terry笔记]redhat5.5_11gR2_RAC_安装
  6. 洛谷—— P2701 [USACO5.3]巨大的牛棚Big Barn
  7. 基于json数据格式实现的简单数据库——jsonDB
  8. MapReduce中combine、partition、shuffle的作用是什么
  9. iOS:界面上下空出黑条
  10. [Project Euler 429] Sum of squares of unitary divisors(数论)