上一篇博客讲过,当多个线程访问共享的可变变量的时候,可以使用锁来进行线程同步。那么如果线程安全性存在的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. Pytorch搭建简单神经网络 Task2

    1>建立数据集(并绘制图像) # -*- coding: utf-8 -*- #demo.py import torch import torch.nn.functional as F # 主要 ...

  2. [POJ2823][洛谷P1886]滑动窗口 Sliding Window

    题目大意:有一列数,和一个窗口,一次能框连续的s个数,初始时窗口在左端,不断往右移动,移到最右端为止,求每次被框住的s个数中的最小数和最大数. 解题思路:这道题是一道区间查询问题,可以用线段树做.每个 ...

  3. 紫书 习题 11-12 UVa 1665 (并查集维护联通分量)

    这道题要逆向思维 反过来从大到小枚举, 就是在矩阵中一点一点加进去数字,这样比较 好操作, 如果正着做就要一点一点删除数字, 不好做. 我们需要在这个过程中维护联通块的个数, 这里用到了并查集. 首先 ...

  4. docker 下修改 mysql sql_mode和配置文件

    原文:docker 下修改 mysql sql_mode和配置文件 打开PowerShell 首先创建mysql容器,这里我们指定使用mysql5.7的版本 docker run -d -p 3306 ...

  5. System and method for assigning a message

    A processor of a plurality of processors includes a processor core and a message manager. The messag ...

  6. VS2015--win32project配置的一些想法之cmake

    近期两天都在配置一个win32项目.为了实现跨平台,写底层代码的人仅仅build了安卓和ios的工程,没有build win32项目. 因为对一些库不是非常了解.配置起来非常困难,心力交瘁. 为了实现 ...

  7. ORA-06553:PLS-306:wrong number or types of arguments in call to &#39;&#39;

    1.错误描写叙述 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/ ...

  8. leetcode——Insertion Sort List 对链表进行插入排序(AC)

    Sort a linked list using insertion sort. class Solution { public: ListNode *insertionSortList(ListNo ...

  9. css实现上下左右布局

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  10. Android 中如何使用 enum / 枚举

    如何在Android开发中合理的使用enum 欢迎大家访问我的Github开源库,这里有好玩的App源码,想和大家分享.https://github.com/ChoicesWang 转载请注明:htt ...