总结了线程安全性的二十四个精华问题

 

1、对象的状态:对象的状态是指存储在状态变量中的数据,对象的状态可能包括其他依赖对象的域。在对象的状态中包含了任何可能影响其外部可见行为的数据。

 

2、一个对象是否是线程安全的,取决于它是否被多个线程访问。这指的是在程序中访问对象的方式,而不是对象要实现的功能。当多个线程访问某个状态变量并且其中有一个线程执行写入操作时,必须采用同步机制来协同这些线程对变量的访问。同步机制包括synchronized、volatile变量、显式锁、原子变量。

 

3、有三种方式可以修复线程安全问题:

1)不在线程之间共享该状态变量

2)将状态变量修改为不可变的变量

3)在访问状态变量时使用同步

 

4、线程安全性的定义:当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步,这个类都能表现出正确的行为,那么就称这个类是线程安全的。

 

5、无状态变量一定是线程安全的,比如局部变量。

 

6、读取-修改-写入操作序列,如果是后续操作是依赖于之前读取的值,那么这个序列必须是串行执行的。在并发编程中,由于不恰当的执行时序而出现不正确的结果是一种非常重要的情况,它称为竞态条件(Race Condition)。最常见的竞态条件类型就是先检查后执行的操作,通过一个可能失效的观测结果来决定下一步的操作。


7、复合操作:要避免竞态条件问题,就必须在某个线程修改该变量时,通过某种方式防止其他线程使用这个变量,从而确保其他线程只能在修改操作完成之前或之后读取和修改状态,而不是在修改状态的过程中。假定有两个操作A和B,如果从执行A的线程看,当另一个线程执行B时,要么将B全部执行完,要么完全不执行B,那么A和B对彼此来说就是原子的。原子操作是指,对于访问同一个状态的所有操作来说,这个操作是一个以原子方式执行的操作。

为了确保线程安全性,读取-修改-写入序列必须是原子的,将其称为复合操作。复合操作包含了一组必须以原子方式执行的接口以确保线程安全性。

 

8、在无状态的类中添加一个状态时,如果这个状态完全由线程安全的对象来管理,那么这个类仍然是线程安全的。(比如原子变量)

 

9、如果多个状态是相关的,需要同时被修改,那么对多个状态的操作必须是串行的,需要进行同步。要保持状态的一致性,就需要在单个原子操作中更新所有相关的状态变量。

 

10、内置锁:synchronized(object){同步块}

Java的内置锁相当于一种互斥体,这意味着最多只有一个线程能持有这种锁,当线程A尝试获取一个由线程B持有的锁时,线程A必须等待或阻塞,直到线程B释放这个锁。如果B永远不释放锁,那么A也将永远地等待下去。

 

11、重入:当某个线程请求一个由其他线程持有的锁时,发出请求的线程就会阻塞。然而,由于内置锁是可重入的,因此如果某个线程试图获得一个已经由它自己持有的锁,那么这个请求就会成功。重入意味着获取锁的操作的粒度是线程,而不是调用。重入的一种实现方法是,为每个锁关联一个获取计数值和一个所有者线程。当计数值为0时,这个锁就被认为是没有被任何线程持有。当线程请求一个未被持有的锁时,JVM将记下锁的持有者,并且将获取计数值置1。如果一个线程再次获取这个锁,计数值将递增,而当线程退出同步代码块时,计数值会相应递减。当计数值为0时,这个锁将被释放。

 

12、对于可能被多个线程同时访问的可变状态变量,在访问它时都需要持有同一个锁,在这种情况下,我们称状态变量是由这个锁保护的。

每个共享的和可变的变量都应该只由一个锁来保护,从而使维护人员知道是哪一个锁。

一种常见的加锁约定是,将所有的可变状态都封装在对象内部,并提供对象的内置锁(this)对所有访问可变状态的代码路径进行同步。在这种情况下,对象状态中的所有变量都由对象的内置锁保护起来。

 

13、不良并发:要保证同步代码块不能过小,并且不要将本应是原子的操作拆分到多个同步代码块中。应该尽量将不影响共享状态且执行时间较长的操作从同步代码块中分离出去,从而在这些操作的执行过程中,其他线程可以访问共享状态。

 

14、可见性:为了确保多个线程之间对内存写入操作的可见性,必须使用同步机制。

 

15、加锁与可见性:当线程B执行由锁保护的同步代码块时,可以看到线程A之前在同一个同步代码块中的所有操作结果。如果没有同步,那么就无法实现上述保证。加锁的含义不仅仅局限于互斥行为,还包括内存可见性。为了确保所有线程都能看到共享变量的最新值,所有执行读操作或写操作的线程都必须在同一个锁上同步。

 

16、volatile变量:当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。volatile变量不会被缓存在寄存器或其他对处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。volatile的语义不足以确保递增操作的原子性,除非你能确保只有一个线程对变量执行写操作。原子变量提供了“读-改-写”的原子操作,并且常常用做一种更好的volatile变量。

 

17、加锁机制既可以确保可见性,又可以确保原子性,而volatile变量只能确保可见性。

 

18、当且仅当满足以下的所有条件时,才应该使用volatile变量:

1)对变量的写入操作不依赖变量的当前值(不存在读取-判断-写入序列),或者你能确保只有单个线程更新变量的值

2)该变量不会与其他状态变量一起纳入不可变条件中

3)在访问变量时不需要加锁

 

19、栈封闭:在栈封闭中,只能通过局部变量才能访问对象。维护线程封闭性的一种更规范的方法是使用ThreadLocal,这个类能使线程的某个值与保存值的对象关联起来,ThreadLocal通过了get和set等访问接口或方法,这些方法为每个使用该变量的线程都存有一份独立的副本,因此get总是返回由当前执行线程在调用set时设置的最新值。

 

20、在并发程序中使用和共享对象时,可以使用一些使用的策略,包括:

1)线程封闭:线程封闭的对象只能由一个线程拥有,对象被封闭在该线程中,并且只能由这个线程修改。

2)只读共享:在没有额外同步的情况下,共享的只读对象可以由多个线程并发访问,但任何线程都不能修改它。共享的只读对象包括不可变对象和事实不可变对象(从技术上来说是可变的,但其状态在发布之后不会再改变)。

3)线程安全共享。线程安全的对象在其内部实现同步,因此多个线程可以通过对象的公有接口来进行访问而不需要进一步的同步。

4)保护对象。被保护的对象只能通过持有对象的锁来访问。保护对象包括封装在其他线程安全对象中的对象,以及已发布并且由某个特定锁保护的对象。

 

21、饥饿:当线程由于无法访问它所需要的资源而不能继续执行时,就发生了饥饿(某线程永远等待)。引发饥饿的最常见资源就是CPU时钟周期。比如线程的优先级问题。在Thread API中定义的线程优先级只是作为线程调度的参考。在Thread API中定义了10个优先级,JVM根据需要将它们映射到操作系统的调度优先级。这种映射是与特定平台相关的,因此在某个操作系统中两个不同的Java优先级可能被映射到同一优先级,而在另一个操作系统中则可能被映射到另一个不同的优先级。

当提高某个线程的优先级时,可能不会起到任何作用,或者也可能使得某个线程的调度优先级高于其他线程,从而导致饥饿。

通常,我们尽量不要改变线程的优先级,只要改变了线程的优先级,程序的行为就将与平台相关,并且会导致发生饥饿问题的风险。

 

事务T1封锁了数据R,事务T2又请求封锁R,于是T2等待。T3也请求封锁R,当T1释放了R上的封锁后,系统首先批准了T3的请求,T2仍然等待。然后T4又请求封锁R,当T3释放了R上的封锁之后,系统又批准了T的请求......T2可能永远等待

 

22、活锁

活锁是另一种形式的活跃性问题,该问题尽管不会阻塞线程,但也不能继续执行,因为线程将不断重复执行相同的操作,而且总会失败。活锁通常发生在处理事务消息的应用程序中。如果不能成功处理某个消息,那么消息处理机制将回滚整个事务,并将它重新放到队列的开头。虽然处理消息的线程并没有阻塞,但也无法继续执行下去。这种形式的活锁通常是由过度的错误恢复代码造成的,因为它错误地将不可修复的错误作为可修复的错误。

 

当多个相互协作的线程都对彼此进行响从而修改各自的状态,并使得任何一个线程都无法继续执行时,就发生了活锁。要解决这种活锁问题,需要在重试机制中引入随机性。在并发应用程序中,通过等待随机长度的时间和回退可以有效地避免活锁的发生。

 

23、当在锁上发生竞争时,竞争失败的线程肯定会阻塞。JVM在实现阻塞行为时,可以采用自旋等待(Spin-Waiting,指通过循环不断地尝试获取锁,直到成功),或者通过操作系统挂起被阻塞的线程。这两种方式的效率高低,取决于上下文切换的开销以及在成功获取锁之前需要等待的时间。如果等待时间较短,则适合采用自旋等待的方式,而如果等待时间较长,则适合采用线程挂起方式。

 

24、有两个因素将影响在锁上发生竞争的可能性:锁的请求频率,以及每次持有该锁的时间。如果二者的乘积很小,那么大多数获取锁的操作都不会发生竞争,会因此在该锁上的竞争不会对可伸缩性造成严重影响。然而,如果在锁上的请求量很高,那么需要获取该锁的线程将被阻塞并等待。在极端情况下,即使仍有大量工作等待完成,处理器也会被闲置。

有3种方式可以降低锁的竞争程度:

1)减少锁的持有时间:

①缩小锁的范围,将与锁无关的代码移出同步代码块,尤其是开销较大的操作以及可能被阻塞的操作(IO操作)。

当把一个同步代码块分解为多个同步代码块时,反而会对性能提升产生负面影响。在分解同步代码块时,理想的平衡点将与平台相关,但在实际情况中,仅可以将一些大量的计算或阻塞操作从同步代码块移出时,才应该考虑同步代码块的大小。

②减小锁的粒度:锁分解和锁分段

锁分解是采用多个相互独立的锁来保护独立的状态变量,从而改变这些变量在之前由单个锁来保护的情况。这些技术能减小锁操作的粒度,并能实现更高的可伸缩性,然而,使用的锁越多,那么发生死锁的风险也就越高。

锁分段:比如JDK1.7及之前的ConcurrentHashMap采用的方式就是分段锁的方式。

2)降低锁的请求频率

3)使用带有协调机制的独占锁,这些机制允许更高的并发性

比如读写锁,并发容器等

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/444762.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何在Appscale下发布自己的应用(二)

本文开始讲如何发布自己的app应用到appscle上 建好appscle网站后,可以在命令行通过 appscle deploy apppathname 来发布自己应用。 除了用命令行提交应用之外,还可以通过appscale的网站直接提交,选择 upload application->选择上传文件-&g…

Python模块(7)-SciPy 简易使用教程

SciPy 简易使用教程1. 符号计算2. 函数向量化3. 波形处理scipy.signal3.1 滤波器3.2 波峰定位基于numpy的一个高级模块,为数学,物理,工程等方面的科学计算提供无可替代的支持。 做重要的思想是:符号计算和函数向量化 1. 符号计算…

Xcode的Architectures和Valid Architectures的区别

目录[-] Xcode的Architectures和Valid Architectures的区别 Architectures Valid Architectures 原因解释如下: 参考1: 所有IOS设备详情列表 List of iOS devices - Wikipedia, the free encyclopedia 参考2: iOS 7: 如何为iPhone 5S编译64位…

Python模块(8)-sklearn 简易使用教程

sklearn 简易使用教程1.scikit-learn的数据集2.scikit-learn 的训练和预测scikit-learn 是在Numpy,SciPy,Matplotlib三个模块上编写的,数据挖掘和数据分析的一个简单有效的工具。scikit-learn包括6大功能:分类,回归,聚类&#xff…

如何发布GAE的应用(一)

安装googleSDK的环境: 1 下载安装包从官网下载 https://cloud.google.com/sdk/downloads -> https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-170.0.0-windows-x86_64-bundled-python.zip 2 如果本地安装了python,直…

leetcode887 鸡蛋掉落

你将获得 K 个鸡蛋&#xff0c;并可以使用一栋从 1 到 N 共有 N 层楼的建筑。 每个蛋的功能都是一样的&#xff0c;如果一个蛋碎了&#xff0c;你就不能再把它掉下去。 你知道存在楼层 F &#xff0c;满足 0 < F < N 任何从高于 F 的楼层落下的鸡蛋都会碎&#xff0c;…

Docker 的日志相关整理

1 Docker daemon日志的位置 Docker daemon日志的位置&#xff0c;根据系统不同各不相同。 Ubuntu - /var/log/upstart/docker.logBoot2Docker - /var/log/docker.logDebian GNU/Linux - /var/log/daemon.logCentOS - /var/log/daemon.log | grep dockerFedora - journalctl -u…

PaperNotes(15)-图神经网络、PyG极简版入门笔记

图神经网络概况1.GNN,GCN,GE的区别2.图卷积的通式--矩阵该如何作用2.1实现12.2实现22.3实现33.PyTorch geometric3.1 PyG内置数据集3.1.1ENZYMES dataset3.1.2Cora3.2 PyG自定义数据集3.2.1Data构建简单的图结构3.2.2 Dataset3.2.3 InMemoryDataset一文读懂图卷积GCN(https://z…

leetcode76 最小覆盖子串

给你一个字符串 S、一个字符串 T&#xff0c;请在字符串 S 里面找出&#xff1a;包含 T 所有字母的最小子串。 示例&#xff1a; 输入: S "ADOBECODEBANC", T "ABC" 输出: "BANC" 说明&#xff1a; 如果 S 中不存这样的子串&#xff0c;则返…

Unity的匹配系统

这个匹配系统是指一个玩家&#xff0c;可以创建一个自己随意命名的房间&#xff0c;然后其他玩家可以通过联网去搜索房间&#xff0c;然后加入房间一起游戏 我先讲讲怎么使用这个匹配系统&#xff1a; 在运行游戏后&#xff0c;因为添加了Network Manager HUD组件&#xff0c;所…

PaperNotes(16)-图神经网络GNN简史、不动点建模-笔记

图神经网络简史、简介1.图神经网络简史2.图神经网络--学习过程3.图神经网络--理论基础4.图神经网络的局限5.GNN,RNN,GGNN6.小结阅读笔记&#xff1a;从图(Graph)到图卷积(Graph Convolution)&#xff1a;漫谈图神经网络模型 (一)(https://www.cnblogs.com/SivilTaram/p/graph_n…

Matchmaker

Unity的多玩家网络功能包含了玩家在因特网上互相玩而不需要公共IP地址的服务。用户可以创建游戏,获取活动游戏列表;加入并退出游戏。当在internet上玩时,网络流量将通过云中的Unity,而不是直接在客户端之间进行。这就避免了防火墙和NATs的问题,几乎可以在任何地方玩游戏。 …

PaperNotes(17)-图卷积神经网络GCN-笔记

图卷积神经网络GCN-笔记1.卷积是什么2.图卷积的源起3.空域卷积3.1消息传递网络MPNN3.2 图采样与聚合GraphSage4.频域卷积5.图结构的序列化-Patch-SAN从图(Graph)到图卷积(Graph Convolution)&#xff1a;漫谈图神经网络模型 (二)(https://www.cnblogs.com/SivilTaram/p/graph_n…

Servlet 工程 web.xml 中的 servlet 和 servlet-mapping 标签

摘录某个工程的 web.xml 文件片段&#xff1a;访问顺序为1—>2—>3—>4&#xff0c;其中2和3的值必须相同。 url-pattern 标签中的值是要在浏览器地址栏中输入的 url&#xff0c;可以自己命名&#xff0c;这个 url 访问名为 servlet-name 中值的 servlet&#xff0c;两…

leetcode236 二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个结点 p、q&#xff0c;最近公共祖先表示为一个结点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度尽可能大&#xff08;一个节点也可以是它自己的…

Unity的 UNet组件介绍

UNet常见概念简介 Spawn:简单来说,把服务器上的GameObject,根据上面的NetworkIdentity组件找到对应监视连接,在监视连接里生成相应的GameObject.Command:客户端调用,服务器执行,这样客户端调用的参数必需要UNet可以序列化,这样服务器在执行时才能把参数反序列化。需要注意…

MachineLearning(10)-聚类

聚类1.K-mean2.系统聚类3.DBSCAN聚类算法聚类&#xff1a;无监督学习&#xff0c;将相似的样本聚为一类。核心如何定义相似。分类&#xff1a;有监督学习&#xff0c;依据分类准则&#xff0c;将样本划分为不同的类。核心分类器的设计&#xff08;KNN&#xff09;聚类&#xff…

帧同步和状态同步(一)

帧同步 什么是帧同步&#xff1a;帧同步常被RTS(即时战略)游戏常采用。在游戏中同步的是玩家的操作指令&#xff0c;操作指令包含当前的帧索引。一般的流程是客户端上传操作到服务器&#xff0c; 服务器收到后并不计算游戏行为&#xff0c; 而是转发到所有客户端。这里最重要的…

帧同步和状态同步(二)案例分析

转自&#xff1a;http://www.gameres.com/489361.html 腾讯一下出了两款MOBA游戏&#xff0c;全民超神&#xff0c;王者荣耀&#xff0c;玩了一下&#xff0c;效果不错&#xff0c;就分析了一下它底层的一些技术&#xff0c;发现一个是采用的状态同步&#xff0c;TCP协议&#…

leetcode279 完全平方数

给定正整数 n&#xff0c;找到若干个完全平方数&#xff08;比如 1, 4, 9, 16, ...&#xff09;使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。 示例 1: 输入: n 12 输出: 3 解释: 12 4 4 4. 示例 2: 输入: n 13 输出: 2 解释: 13 4 9. 思路&#xf…