并发技巧清单
如何尽量保证线程安全
-
可变状态是至关重要的。
所有并发访问都可以归结为如何协调对并发状态的访问,可变状态越少,越容易确保线程安全性。 -
尽量将域声明为final类型,除非需要它们是可变的。
-
不可变对象一定是线程安全的。
不可变对象能极大地降低并发编程的复杂性。它们更为简单且安全,可以任意共享而无须使用加锁或保护性复制等机制。
-
封装有助于管理复杂性。 在编写线程安全的程序时,虽然可以将所有数据都保存在全局变量,但为什么要这样做? 将数据封装在对象中,更易于维护不变性条件:将同步机制封装在对象中,更易于遵循同步策略。
-
用锁保护每个可变变量。
-
当保护同一个不变性条件中的所有变量时,要使用同一个锁。
-
在执行复合操作期间,要持有锁。
-
如果从多个线程中访问同一个可变变量时没有同步机制,那么程序会可能出问题。
-
不要故作聪明地推断不需要使用同步。
-
在设计过程中考虑线程安全,或者在文档中明确地指出塔不是线程安全的。
-
将同步策略文档化。
-
使用ThreadLocal保存状态变量
避免死锁
1.对于资源的加锁时间必须足够短,也就是必要时进行锁
2.访问资源过程中的锁需要按照一致的顺序进行获取,否则需要提升出一个更大的锁来确保资源的获取
3.尽量通过封装的形式,避免将锁暴露给外部,从而造成不必要的资源死锁
4.多数情况下,死锁是由于获取锁的顺序错误锁导致的。
5.避免一个线程同时获取多个锁。
6.避免一个线程在锁内同时占用多个资源,尽量保持一个锁只占用一个资源。
7.尝试使用定时锁,使用lock.tryLock(timeout)来替代使用内部锁机制。 8.
原子性
原子(atom)本意是“不能被进一步分割的最小粒子”,而原子操作(atomic operation)意为"不可被中断的一个或一系列操作" 。 在多处理器上实现原子操作就变得有点复杂。
对象类型
- 对象地址原子读写,线程安全。
- 并发读不可变状态,线程安全。
- 并发读写可变状态,非线程安全。
基本类型
- int,char数值读写,线程安全。
- long,double高低位,非线程安全。
- i++ 等组合操作,非线程安全。
可见性
final
- 初始化final字段确保可见性
volatile
- 读写volatile字段确保可见性
synchronized
- 同步块内读写字段确保可见性
happen before
- 遵守happen before次序可见性
可排序性
Happen Before法则
-
程序次序法则 如果A一定在B之前发生,happen before
-
监视器法则 对一个监视器的解锁一定发生在后续对同一个监视器加锁之前
-
Volatile变量法则 写volatile变量一定发生在后续对它读之前
-
线程启动法则 Thread.start一定发生在线程中的动作之前
-
线程终结法则 线程中的任何动作一定发生在以下操作的动作之前。
其它线程检测到这个线程已经终止,从Thread.join调用成功返回,Thread.isAlive()返回false
-
中断法则 一个线程调用另一个线程的interrupt一定发生在另一线程发现中断之前
-
终结法则 一个对象的构造函数结束一定发生在对象的finalizer之前
-
传递性 A发生在B之前,B发生在C之前,A一定发生在C之前
如果多个线程访问同一个可变的状态变量时,没有使用合适的同步,那么程序就会出现错误。有三种方式可以修复这个问题:
- 不在线程之间共享该状态变量
- 将状态变量修改为不可变的变量
- 在访问状态变量时使用同步
什么是线程安全性
在线程安全性的定义中,最核心的概念就是正确性。 当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调用代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的。
在线程安全类中封装了必要的同步机制,因此客户端无需进一步采取同步措施。
无状态对象一定是线程安全的。
熟练使用线程安全类
要保持状态的一致性,就需要在单个原子操作中更新所有相关的状态变量。
并非所有的数据都需要锁的保护,只有被多个线程同时访问的可变数据才需要通过锁来保护。
对非原子的64位操作,如long,double类型的变量,在多线程下存在这样的共享变量时,请把变量定义成volatile
加锁机制既可以确保可见性又可以确保原子性 ,volatile变量只可以确保可见性。
当访问共享的可变数据时,通常需要同步,一种避免使用同步方式就是不共享数据。
满足不可变对象的条件:
- 对象创建以后其状态不能修改
- 对象的所有域都是final类型
- 对象是正确创建的(在对象创建的期间,this引用没有逸出)
final域能确保初始化过程中的安全性
不变模式
在并行程序开发过程中,同步操作似乎是必不可少的。当多线程对同一个对象进行读写操作时,为了保证数据对象对一致性 和正确性,有必要对对象使用同步。故而同步操作对性能有相当对消耗。为了尽可能的去除这些同步操作,提高并行程序的 性能,可以使用一种不可变的对象,依靠对象的不变性,可以确保其在没有使用同步操作的多线程环境下依然始终保持 内部状态的一致性和正确性。
不变模式天生就是多线程友好的,它的核心思想是,一个对象一旦被创建,则它的内部状态将永远不会发生改变。所以,没有 一个线程可以修改其内部状态和数据。
不变模式使用场景介绍
- 当对象被创建后,其内部状态和数据不再发生任何变化
- 对象需要被共享、被多线程频繁访问
JAVA的并发控制方式
- 内部锁
- 重入锁
- 读写锁
- ThreadLocal变量
- 信号量
隐藏比较深的数据竞争
竞争情况描述 | 线程T1 | 线程T2 |
---|---|---|
编译器将这个表达式扩展成temp=x ,和 x=temp+1 | x+=1 | x+=2 |
下标i和j相同时可能会出现数据竞争 | a[i]+=1 | a[j]+=1 |
指针q和p指向同一个目标时可能会出现数据竞争 | *q+=2 | *p+=1 |
foo函数可能使用参数对一个共享变量进行修改 | foo(1) | foo(2) |
即使在指令集,硬件还是会将【edi】对更新操作扩展成独立对对读操作和写操作改 | add [edi],1 | add [edi] ,2 |
欢迎工作一到五年的Java工程师朋友们加入Java架构开发:468947140
点击链接加入群聊【Java-BATJ企业级资深架构】:https://jq.qq.com/?_wv=1027&k=5zMN6JB
本群提供免费的学习指导 架构资料 以及免费的解答
不懂得问题都可以在本群提出来 之后还会有职业生涯规划以及面试指导
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
云栖科技评论64期:俄罗斯世界杯,新技术再一次升级内容产业
【卷首语】俄罗斯世界杯,新技术再一次升级内容产业 以1470年在德国科隆发行的全世界第一份报纸计算,内容产业至今已经发展了超过500年,并逐渐发展成为一种以数字化技术、多媒体技术和网络技术为基础,利用信息资源和其他相关资源,创作、开发、分发、销售和消费信息产品与服务的产业。 在内容产业的发展历程中,新技术一直是内容产业升级的“触发器”和“催化剂”,仅以近十几年的发展来看,互联网的出现塑造了网络视频、网络新闻、网络文学等全新业态;数字化技术催生了电子书、有声书、数字音乐等新兴载体;此外,智能手机等移动设备和3G、4G高速移动通信网络的出现,让内容产业从此可以实现随身移动、随时获取、持续供给的颠覆性服务形式。 在本届世界杯期间,以人工智能、大数据、云计算为代表的新一代信息技术,再一次触发了内容产业的升级再造: 在CCTV5新媒
- 下一篇
漫谈OceanBase 列式存储
列式存储主要的目的有两个: 大部分OLAP查询只需要读取部分列而不是全部列数据,列式存储可以避免读取无用数据; 将同一列的数据在物理上存放在一起,能够极大地提高数据压缩率。 OLAP和OLTP OLAP,也叫联机分析处理(Online Analytical Processing)系统,有的时候也叫DSS决策支持系统,就是我们说的数据仓库。在这样的系统中,语句的执行量不是考核标准,因为一条语句的执行时间可能会非常长,读取的数据也非常多。所以,在这样的系统中,考核的标准往往是磁盘子系统的吞吐量(带宽),如能达到多少MB/s的流量。 在OLAP系统中,常使用分区技术、并行技术。 分区技术在OLAP系统中的重要性主要体现在数据库管理上,比如数据库加载,可以通过分区交换的方式实现,备份可以通过备份分区表空间实现,删除数据可以通过分区进行删除,至于分区在性能上的影响,它可以使得一些大表的扫描变得很快(只扫描单个分区)。另外,如果分区结合并行的话,也可以使得整个表的扫描会变得很快。总之,分区主要的功能是管理上的方便性,它并不能绝对保证查询性能的提高,有时候分区会带来性能上的提高,有时候会降低。 在O...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
-
Docker使用Oracle官方镜像安装(12C,18C,19C)
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8编译安装MySQL8.0.19
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
推荐阅读
最新文章
- CentOS7,CentOS8安装Elasticsearch6.8.6
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- CentOS6,CentOS7官方镜像安装Oracle11G
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- 设置Eclipse缩进为4个空格,增强代码规范
- Mario游戏-低调大师作品
- MySQL8.0.19开启GTID主从同步CentOS8
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果