JAVA并发-内置锁和ThreadLocal
上一篇博客讲过,当多个线程访问共享的可变变量的时候,可以使用锁来进行线程同步。那么如果线程安全性存在的3个前提条件不同时存在的话,自然就不需要考虑线程安全性了。或者说如果我们能够将某个共享变量变为局部变量,那么自然线程安全性问题就不存在了。
我们把“诸如将全局变量变为局部变量”这种将某个对象封闭在一个线程中的技术称为线程封闭,在《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(每个线程在干自己的事情)。
综上,个人觉得使用锁还是线程封闭去解决线程安全问题,终究是业务逻辑的不同,或者说是代码功能的不同。我们要掌握的就是有这些个解决代码安全行的方法,然后放到具体的场景下去应用。
JAVA并发-内置锁和ThreadLocal的更多相关文章
- java 并发——内置锁
坚持学习,总会有一些不一样的东西. 一.由单例模式引入 引用一下百度百科的定义-- 线程安全是多线程编程时的计算机程序代码中的一个概念.在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同 ...
- java synchronized内置锁的可重入性和分析总结
最近在读<<Java并发编程实践>>,在第二章中线程安全中降到线程锁的重进入(Reentrancy) 当一个线程请求其它的线程已经占有的锁时,请求线程将被阻塞.然而内部锁是可重 ...
- Java 并发:内置锁 Synchronized
摘要: 在多线程编程中,线程安全问题是一个最为关键的问题,其核心概念就在于正确性,即当多个线程訪问某一共享.可变数据时,始终都不会导致数据破坏以及其它不该出现的结果. 而全部的并发模式在解决问题时,採 ...
- Java内置锁和简单用法
一.简单的锁知识 关于内置锁 Java具有通过synchronized关键字实现的内置锁,内置锁获得锁和释放锁是隐式的,进入synchronized修饰的代码就获得锁,走出相应的代码就释放锁. jav ...
- 转:【Java并发编程】之一:可重入内置锁
每个Java对象都可以用做一个实现同步的锁,这些锁被称为内置锁或监视器锁.线程在进入同步代码块之前会自动获取锁,并且在退出同步代码块时会自动释放锁.获得内置锁的唯一途径就是进入由这个锁保护的同步代码块 ...
- 【Java并发编程】之一:可重入内置锁
每个Java对象都可以用做一个实现同步的锁,这些锁被称为内置锁或监视器锁.线程在进入同步代码块之前会自动获取锁,并且在退出同步代码块时会自动释放锁.获得内置锁的唯一途径就是进入由这个锁保护的同步代码块 ...
- 《java并发编程实战》读书笔记1--线程安全性,内置锁,重入,状态
什么是线程安全? 当多个线程访问某个类时,不管这些的线程的执行顺序如何,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的. 哈哈书上的解释,还是翻译过 ...
- java并发编程(一)可重入内置锁
每个Java对象都可以用做一个实现同步的锁,这些锁被称为内置锁或监视器锁.线程在进入同步代码块之前会自动获取锁,并且在退出同步代码块时会自动释放锁.获得内置锁的唯一途径就是进入由这个锁保护的同步代码块 ...
- 深入理解java内置锁(synchronized)和显式锁(ReentrantLock)
多线程编程中,当代码需要同步时我们会用到锁.Java为我们提供了内置锁(synchronized)和显式锁(ReentrantLock)两种同步方式.显式锁是JDK1.5引入的,这两种锁有什么异同呢? ...
随机推荐
- eclipse历史版本下载地址
http://wiki.eclipse.org/Older_Versions_Of_Eclipse
- Spring Boot基础教程》 第1节工具的安装和使用
<Spring Boot基础教程> 第1节 工具的安装和使用 Spring Boot文档 https://qbgbook.gitbooks.io/spring-boot-reference ...
- 特斯拉正加快部署第三代Autopilot自动驾驶计算机
新浪科技讯,北京时间 4 月 10 日下午消息,据电动汽车新闻网站 Electrek 报道,特斯拉正在加快部署第三代 Autopilot 自动驾驶计算机,因为当前一代产品的计算能力即将被耗尽. 201 ...
- Pimple相关的源码
已经有了非常好的Pimple的相关解析,建议先看下:Pimple - 一个简单的 PHP 依赖注入容器读 PHP - Pimple 源码笔记(上)读 PHP - Pimple 源码笔记(下) 这里通过 ...
- 【Henu ACM Round#24 B】Gargari and Bishops
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 如果写过n皇后问题. 肯定都知道 某个点(i,j)和它在同一条对角线上的点分别是i+j的值和i-j的值相同的点. 然后会发现选择的两 ...
- [Transducer] Create a Sequence Helper to Transduce Without Changing Collection Types
A frequent use case when transducing is to apply a transformation to items without changing the type ...
- POJ 3207 Ikki's Story IV - Panda's Trick(2-sat)
POJ 3207 Ikki's Story IV - Panda's Trick id=3207" target="_blank" style=""& ...
- 软件project之软件设计
英雄是随着历史的时代产生的.软工也不例外.软件project这一门学科主要是为了解决当代软件危机而诞生的, 学习软件project的视频过后,最终让我揭开了它的神奇面纱,让我对软工设计有了一个初步的认 ...
- 一般处理程序cookie和session+末尾的多选框,下拉框
登录页面<body><form action="Login.ashx" method="post"><input type=&qu ...
- PostgreSQL Replication之第四章 设置异步复制(2)
4.2 配置级联复制 正如您在本章已经看到的,设置流复制真的很容易.只需要设置几个参数,做一个基础备份,并享受您的复制设置. 在许多情况下,这种情况更有一点点微妙.在这个例子中我们假设:我们要使用一个 ...