上一篇博客讲过,当多个线程访问共享的可变变量的时候,可以使用锁来进行线程同步。那么如果线程安全性存在的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. 每个人都能实现的vue自定义指令

    前文 先来bb一堆废话哈哈.. 用vue做项目也有一年多了.除了用别人的插件之外.自己也没尝试去封装指令插件之类的东西来用. 刚好最近在项目中遇到一个问题.(快速点击按钮多次触发多次绑定的方法),于是 ...

  2. WPF打字机效果

    public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); TypewriteTex ...

  3. 洛谷 P2128 赤壁之战

    P2128 赤壁之战 题目描述 赤壁之战,黄盖率舰满载薪草膏油诈降曹军. 受庞统所授的连环计,曹军战船之间由铁索相连,没有两艘战船在同一位置,也没有铁索两两相交或穿过战船.每艘船都有其一定的战略价值. ...

  4. 你必须了解的RecyclerView的五大开源项目-解决上拉加载、下拉刷新和添加Header、Footer等问题

    前段时间做项目由于采用的MD设计,所以必须要使用RecyclerView全面代替ListView.但是开发中遇到了需要实现RecyclerView上拉加载.下拉刷新和添加Header以及Footer等 ...

  5. ubuntu下不用拔盘就可以重新识别usb设备

    #!/bin/sh # Usage: ./resetusb ARGUMENT(The keyword for your usb device) var1=$ keyword=${var1:=Stora ...

  6. thinkphp5项目--个人博客(三)

    thinkphp5项目--个人博客(三) 项目地址 fry404006308/personalBlog: personalBloghttps://github.com/fry404006308/per ...

  7. [SDOI2010] 古代猪文 (快速幂+中国剩余定理+欧拉定理+卢卡斯定理) 解题报告

    题目链接:https://www.luogu.org/problemnew/show/P2480 题目背景 “在那山的那边海的那边有一群小肥猪.他们活泼又聪明,他们调皮又灵敏.他们自由自在生活在那绿色 ...

  8. 安卓开发--HttpDemo01

    package com.cnn.httpdemo01; import android.app.Activity; import android.content.Intent; import andro ...

  9. spring cloud集成 consul源码分析

    1.简介 1.1 Consul is a tool for service discovery and configuration. Consul is distributed, highly ava ...

  10. IE(8~11+) 可用右键加速器

    必应词典工具 立即安装: 网络安装:http://dict.bing.com.cn/tools_dl.aspx?dl=ie8acc&mkt=ZH-CN 开发示例: <?xml versi ...