上一篇博客讲过,当多个线程访问共享的可变变量的时候,可以使用锁来进行线程同步。那么如果线程安全性存在的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(每个线程在干自己的事情)。

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

JAVA并发-内置锁和ThreadLocal的更多相关文章

  1. java 并发——内置锁

    坚持学习,总会有一些不一样的东西. 一.由单例模式引入 引用一下百度百科的定义-- 线程安全是多线程编程时的计算机程序代码中的一个概念.在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同 ...

  2. java synchronized内置锁的可重入性和分析总结

    最近在读<<Java并发编程实践>>,在第二章中线程安全中降到线程锁的重进入(Reentrancy) 当一个线程请求其它的线程已经占有的锁时,请求线程将被阻塞.然而内部锁是可重 ...

  3. Java 并发:内置锁 Synchronized

    摘要: 在多线程编程中,线程安全问题是一个最为关键的问题,其核心概念就在于正确性,即当多个线程訪问某一共享.可变数据时,始终都不会导致数据破坏以及其它不该出现的结果. 而全部的并发模式在解决问题时,採 ...

  4. Java内置锁和简单用法

    一.简单的锁知识 关于内置锁 Java具有通过synchronized关键字实现的内置锁,内置锁获得锁和释放锁是隐式的,进入synchronized修饰的代码就获得锁,走出相应的代码就释放锁. jav ...

  5. 转:【Java并发编程】之一:可重入内置锁

    每个Java对象都可以用做一个实现同步的锁,这些锁被称为内置锁或监视器锁.线程在进入同步代码块之前会自动获取锁,并且在退出同步代码块时会自动释放锁.获得内置锁的唯一途径就是进入由这个锁保护的同步代码块 ...

  6. 【Java并发编程】之一:可重入内置锁

    每个Java对象都可以用做一个实现同步的锁,这些锁被称为内置锁或监视器锁.线程在进入同步代码块之前会自动获取锁,并且在退出同步代码块时会自动释放锁.获得内置锁的唯一途径就是进入由这个锁保护的同步代码块 ...

  7. 《java并发编程实战》读书笔记1--线程安全性,内置锁,重入,状态

    什么是线程安全? 当多个线程访问某个类时,不管这些的线程的执行顺序如何,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的. 哈哈书上的解释,还是翻译过 ...

  8. java并发编程(一)可重入内置锁

    每个Java对象都可以用做一个实现同步的锁,这些锁被称为内置锁或监视器锁.线程在进入同步代码块之前会自动获取锁,并且在退出同步代码块时会自动释放锁.获得内置锁的唯一途径就是进入由这个锁保护的同步代码块 ...

  9. 深入理解java内置锁(synchronized)和显式锁(ReentrantLock)

    多线程编程中,当代码需要同步时我们会用到锁.Java为我们提供了内置锁(synchronized)和显式锁(ReentrantLock)两种同步方式.显式锁是JDK1.5引入的,这两种锁有什么异同呢? ...

随机推荐

  1. How Javascript works (Javascript工作原理) (二) 引擎,运行时,如何在 V8 引擎中书写最优代码的 5 条小技巧

    个人总结: 一个Javascript引擎由一个标准解释程序,或者即时编译器来实现. 解释器(Interpreter): 解释一行,执行一行. 编译器(Compiler): 全部编译成机器码,统一执行. ...

  2. luogu P2252 取石子游戏(威佐夫博弈)

    题意 题解 对于像我这种不知道威佐夫博弈的人来说,拿到题就开始打表了. 然而打完后并没有发现什么. 然后才知道是威佐夫博弈. 结论是当(int)((b-a)*((sqrt(5.0)+1.0)/2.0) ...

  3. Linux系统之间文件传输 scp 命令

    个人使用记录 scp /home/liwm/Downloads/mysql-5.5.32-linux2.6-x86_64.tar.gz root@192.168.122.3:/home/oldboy/ ...

  4. Django学习之配置篇

    MTV Model Template View 数据库 模版文件 业务处理 了解Django框架,功能齐全 一.安装Django&Django基本配置 安装Django pip3 django ...

  5. POJ1338 &amp; POJ2545 &amp; POJ2591 &amp; POJ2247 找给定规律的数

    POJ1338 2545 2591 2247都是一个类型的题目,所以放到一起来总结 POJ1338:Ugly Numbers Time Limit: 1000MS   Memory Limit: 10 ...

  6. Unity 内置Shader变量、辅助函数等

    一:标准库里的常用.cginc文件 HLSLSupport.cginc - (automatically included) Helper macros and definitions for cro ...

  7. Android jni 二维数组 传递

    学习Android Jni时,一个二维 整数 数组的传递花了我好长时间,在网上查的资料都不全,当然最后是成功了,写在这里是为了自己记住,当然有人搜索到并利用了我会很高兴.   in Android J ...

  8. BZOJ 1069 求凸包+旋转卡壳

    思路: 求凸包: 先按照x轴排个序 从左往右扫一遍 找到上凸壳 (用叉积) 再从右往左扫一遍 求下凸壳 搞个旋转卡壳就好啦~ 嗯 我手懒 用的C++ Complex库 巨好用! //By Sirius ...

  9. <Sicily>Inversion Number(线段树求逆序数)

    一.题目描述 There is a permutation P with n integers from 1 to n. You have to calculate its inversion num ...

  10. inception - resnet

    只有reduction-A是共用的,只是改了其中的几个参数 linear是线性激活. 结构是一样的