多线程情况下如何保证线程安全
一、线程安全等级
其实线程安全并不是一个“非黑即白”单项选择题。按照“线程安全”的安全程度由强到弱来排序,我们可以将java语言中各种操作共享的数据分为以下5类:不可变、绝对线程安全、相对线程安全、线程兼容和线程对立。
1、不可变
在java语言中,不可变的对象一定是线程安全的,无论是对象的方法实现还是方法的调用者,都不需要再采取任何的线程安全保障措施。如final关键字修饰的数据不可修改,可靠性最高。
2、绝对线程安全
绝对的线程安全完全满足Brian GoetZ给出的线程安全的定义,这个定义其实是很严格的,一个类要达到“不管运行时环境如何,调用者都不需要任何额外的同步措施”通常需要付出很大的代价。
3、相对线程安全
相对线程安全就是我们通常意义上所讲的一个类是“线程安全”的。
它需要保证对这个对象单独的操作是线程安全的,我们在调用的时候不需要做额外的保障措施,但是对于一些特定顺序的连续调用,就可能需要在调用端使用额外的同步手段来保证调用的正确性。
在java语言中,大部分的线程安全类都属于相对线程安全的,例如Vector、HashTable、Collections的synchronizedCollection()方法保证的集合。
4、线程兼容
线程兼容就是我们通常意义上所讲的一个类不是线程安全的。
线程兼容是指对象本身并不是线程安全的,但是可以通过在调用端正确地使用同步手段来保证对象在并发环境下可以安全地使用。Java API中大部分的类都是属于线程兼容的。如与前面的Vector和HashTable相对应的集合类ArrayList和HashMap等。
5、线程对立
线程对立是指无论调用端是否采取了同步错误,都无法在多线程环境中并发使用的代码。由于java语言天生就具有多线程特性,线程对立这种排斥多线程的代码是很少出现的。
一个线程对立的例子是Thread类的supend()和resume()方法。如果有两个线程同时持有一个线程对象,一个尝试去中断线程,另一个尝试去恢复线程,如果并发进行的话,无论调用时是否进行了同步,目标线程都有死锁风险。正因此如此,这两个方法已经被废弃啦。
二、线程安全的实现方法
保证线程安全以 是否需要同步手段 分类,分为同步方案和无同步方案。
1、互斥同步
互斥同步是最常见的一种并发正确性保障手段。同步是指:在多线程并发访问共享数据时,保证共享数据在同一时刻只被一个线程使用(同一时刻,只有一个线程在操作共享数据)。而互斥是实现同步的一种手段,临界区、互斥量和信号量都是主要的互斥实现方式。因此,在这4个字里面,互斥是因,同步是果;互斥是方法,同步是目的。
在java中,最基本的互斥同步手段就是synchronized关键字,synchronized关键字编译之后,会在同步块的前后分别形成monitorenter和monitorexit这两个字节码指令,这两个字节码指令都需要一个reference类型的参数来指明要锁定和解锁的对象。
此外,ReentrantLock也是通过互斥来实现同步。在基本用法上,ReentrantLock与synchronized很相似,他们都具备一样的线程重入特性。
互斥同步最主要的问题就是进行线程阻塞和唤醒所带来的性能问题,因此这种同步也称为阻塞同步。从处理问题的方式上说,互斥同步属于一种悲观的并发策略,总是认为只要不去做正确地同步措施(例如加锁),那就肯定会出现问题,无论共享数据是否真的会出现竞争,它都要进行加锁。
2、非阻塞同步
随着硬件指令集的发展,出现了基于冲突检测的乐观并发策略,通俗地说,就是先进行操作,如果没有其他线程争用共享数据,那操作就成功了;如果共享数据有争用,产生了冲突,那就再采用其他的补偿措施。(最常见的补偿错误就是不断地重试,直到成功为止),这种乐观的并发策略的许多实现都不需要把线程挂起,因此这种同步操作称为非阻塞同步。
非阻塞的实现CAS(compareandswap):CAS指令需要有3个操作数,分别是内存地址(在java中理解为变量的内存地址,用V表示)、旧的预期值(用A表示)和新值(用B表示)。CAS指令执行时,当且仅当V处的值符合旧预期值A时,处理器用B更新V处的值,否则它就不执行更新,但是无论是否更新了V处的值,都会返回V的旧值,上述的处理过程是一个原子操作。
CAS缺点:
ABA问题:因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。
ABA问题的解决思路就是使用版本号。在变量前面追加版本号,每次变量更新的时候把版本号加一,那么A-B-A就变成了1A-2B-3C。JDK的atomic包里提供了一个类AtomicStampedReference来解决ABA问题。这个类的compareAndSet方法作用是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。
3、无需同步方案
要保证线程安全,并不是一定就要进行同步,两者没有因果关系。同步只是保证共享数据争用时的正确性的手段,如果一个方法本来就不涉及共享数据,那它自然就无需任何同步操作去保证正确性,因此会有一些代码天生就是线程安全的。
1)可重入代码
可重入代码(ReentrantCode)也称为纯代码(Pure Code),可以在代码执行的任何时刻中断它,转而去执行另外一段代码,而在控制权返回后,原来的程序不会出现任何错误。所有的可重入代码都是线程安全的,但是并非所有的线程安全的代码都是可重入的。
可重入代码的特点是不依赖存储在堆上的数据和公用的系统资源、用到的状态量都是由参数中传入、不调用 非可重入的方法等。
(类比:synchronized拥有锁重入的功能,也就是在使用synchronized时,当一个线程得到一个对象锁后,再次请求此对象锁时时可以再次得到该对象的锁)
2)线程本地存储
如果一段代码中所需的数据必须与其他代码共享,那就看看这些共享数据的代码是否能保证在同一个线程中执行?如果能保证,我们就可以把共享数据的可见范围限制在同一个线程之内。这样无需同步也能保证线程之间不出现数据的争用问题。
符合这种特点的应用并不少见,大部分使用消费队列的架构模式(如“生产者-消费者”模式)都会将产品的消费过程尽量在一个线程中消费完。其中最重要的一个应用实例就是经典的Web交互模型中的“一个请求对应一个服务器线程(Thread-per-Request)”的处理方式,这种处理方式的广泛应用使得很多Web服务器应用都可以使用线程本地存储来解决线程安全问题。
原文链接:https://blog.csdn.net/qq_26545305
多线程情况下如何保证线程安全相关推荐
- shiro如何保证session不失效_请问在不加锁的情况下如何保证线程安全?
概念 compare and swap,解决多线程并行情况下使用锁造成性能损耗的一种机制,CAS操作包含三个操作数--内存位置(V).预期原值(A)和新值(B).如果内存位置的值与预期原值相匹配,那么 ...
- Spring Cloud Feign传输Header,并保证多线程情况下也适用
Spring Cloud Feign传输Header,并保证多线程情况下也适用 一.现象 微服务在生产中,常遇到需要把 header 传递到下一子服务的情况(如服务A访问服务B的接口,需要传递head ...
- 两个鸡蛋测试:从100层楼往下扔鸡蛋,求最坏情况下确认保证鸡蛋可以不破的最大楼层所需次数
最坏情况下求得最优解所需的次数 内容说明 本文是在看过<<妙解谷歌压箱底面试题:如何正确的从楼上抛鸡蛋>>一文以后做的总结,该文章对此问题描写的很详细,但是在拜读的过程中也花了 ...
- 集群高并发情况下如何保证分布式唯一全局ID生成
作者:轻狂书生 blog.csdn.net/LookForDream_/article/details/109355335 前言 系统唯一ID是我们在设计一个系统的时候常常会遇见的问题,也常常为这个问 ...
- 【线程池的工作参数、什么情况下会触发最大线程数?什么情况下会回收线程?】
线程池是一个重要的 Java 并发编程工具,用于控制线程的创建.调度和回收,可以有效地提高程序的性能和资源利用率. 线程池的工作参数包括以下几个方面: 核心线程数(corePoolSize): 表示线 ...
- 关于不使用外键(或软删除)的情况下如何保证关联数据完整性的思考
引言 最近在修改公司项目中由于用户数据软删除引发的一系列问题时,对于外键的使用也进行了一波思考. 相信看过阿里开发手册的朋友们应该看到过这一段话 因此特意百度了一番,众说纷纭.当然今天不是为了 ...
- 多线程情况下创建连接池
JAVA数据库连接池实现 博客分类: • 数据库连接池 JAVA数据库连接池连接池原理连接池实现JAVA连接池 连接池的管理用了了享元模式,这里对连接池进行简单设计. 一.设计思路 1.连接池配置属性 ...
- 高并发情况下创建多少线程合适
CPU 密集型程序创建多少个线程合适? 计算(CPU)密集型的线程恰好在某时因为发生一个页错误或者因其他原因而暂停,刚好有一个"额外"的线程,可以确保在这种情况下CPU周期不会中断 ...
- Mysql数据库在多服务器集群的情况下如何保证数据同步?
数据不同步的异常情况通常出现在,在同一时刻有多个写数据的操作发生.因此要避免数据库的数据不同步这个问题,就要避免同时有多个写数据的操作,同时只能有一个写操作. 这在一台服务器运行的时候似乎是没有问题的 ...
最新文章
- 跟我斗图,我用Python爬虫下载几个G的表情砸死你
- aop在项目中的实际运用_【消防验收】防火玻璃在实际运用中的四大关键问题
- CSS如何实现两个a标签元素的文字一个靠左一个靠右,并且能点击分别不同的链接
- cassss服务未启动_电梯启动死机故障处理方法
- 安卓7.0海信定制版新增功能
- 推荐 OS X 下最方便实用的六款解压缩软件
- 基于Springboot的高校课程管理系统 课程设计报告 毕业设计 包括报告和程序包
- 计算机双面打印设置,双面打印怎么设置?双面打印设置方法步骤
- 微信小程序云开发入门到放弃(一)入门篇
- 2018大华软件大赛模拟赛第2题 (网络上有一台流媒体服务器S和一台客户端C,S向C发送流媒体数据。)...
- JS逆向之网易云音乐
- teamviewer JAVA,安装teamviewer失败 更换本地源
- 计算机主板清理,电脑主板脏了如何清洗电脑主板才是正确
- 网站查询服务器租期,游戏服务器租期
- html的国际标准智商测试,国际标准智商测试30题(含答案)
- mysql 查询最近三个月的数据
- 学习人工智能需要哪些必备的数学基础?
- 项目五总结 创建与使用视图
- 基于 Electron + ES6 实现的桌面计算器应用
- 转:程序员风格的修真小说