java 如何保证线程安全

news/2024/10/31 3:30:53/

1 互斥同步

互斥同步是最常见的一种并发正确性保障手段。同步是指在多线程并发访问共享数据时,保证共享数据在同一时刻只被一个线程使用(同一时刻,只有一个线程在操作共享数据)。而互斥是实现同步的一种手段,临界区、互斥量和信号量都是主要的互斥实现方式。因此,在这4个字里面,互斥是因,同步是果;互斥是方法,同步是目的。

在java中,最基本的互斥同步手段就是synchronized关键字,synchronized关键字编译之后,会在同步块的前后分别形成monitorentermonitorexit这两个字节码指令,这两个字节码指令都需要一个reference类型的参数来指明要锁定和解锁的对象。

此外,ReentrantLock也是通过互斥来实现同步。在基本用法上,ReentrantLocksynchronized很相似,它们都具备一样的线程重入特性。

互斥同步最主要的问题就是进行线程阻塞和唤醒所带来的性能问题,因此这种同步也成为阻塞同步。从处理问题的方式上说,互斥同步属于一种悲观的并发策略,总是认为只要不去做正确的同步措施(例如加锁),那就肯定会出现问题,无论共享数据是否真的会出现竞争,它都要进行加锁。

2 非阻塞同步

随着硬件指令集的发展,出现了基于冲突检测的乐观并发策略,通俗地说,就是先进行操作,如果没有其他线程竞争共享数据,那操作就成功了;如果共享数据有竞争关系,产生了冲突,那就再采用其他的补偿措施。(最常见的补偿错误就是不断地重试,直到成功为止),这种乐观的并发策略的许多实现都不需要把线程挂起,因此这种同步操作称为非阻塞同步。

非阻塞的实现 CAS(compare and swap):CAS指令需要有3个操作数,分别是内存地址(在java中理解为变量的内存地址,用V表示)、旧的预期值(用A表示)和新值(用B表示)。CAS指令执行时,当且仅当V处的值符合旧预期值A时,处理器用B更新V处的值,否则它就不执行更新,但是无论是否更新了V处的值,都会返回V的旧值,上述的处理过程是一个原子操作。

CAS缺点

ABA问题:因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。

ABA问题的解决思路就是使用版本号。在变量前面追加版本号,每次变量更新的时候把版本号加1,那么A-B-A就变成了1A-2B-3C。JDK的atomic包里提供了一个类AtomicStampedReference来解决ABA问题。这个类的compareAndSet方法作用是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。

3 无需同步方案

要保证线程安全,并不是一定就要进行同步,两者没有因果关系。 同步只是保证共享数据争用时的正确性的手段,如果一个方法本来就不涉及共享数据,那它自然就无需任何同步操作去保证正确性,因此会有一些代码天生就是线程安全的。

3.1 可重入代码

可重入代码(Reentrant Code)也称为纯代码(Pure Code),可以在代码执行的任何时刻中断它,转而去执行另外一段代码,而在控制权返回后,原来的程序不会出现任何错误。所有的可重入代码都是线程安全的,但是并非所有的线程安全的代码都是可重入的。

可重入代码的特点是不依赖存储在堆上的数据和公用的系统资源、用到的状态量都是由参数中传入、不调用非可重入的方法等。

类比:synchronized拥有锁重入的功能,也就是在使用synchronized时,当一个线程得到一个对象锁后,再次请求此对象锁时时可以再次得到该对象的锁。

3.2 线程本地存储

如果一段代码中所需的数据必须与其他代码共享,那就看看这些共享数据的代码是否能保证在同一个线程中执行?如果能保证,我们就可以把共享数据的可见范围限制在同一个线程之内。这样无需同步也能保证线程之间不出现数据的争用问题。

符合这种特点的应用并不少见,大部分使用消费队列的架构模式(如“生产者-消费者”模式)都会将产品的消费过程尽量在一个线程中消费完。其中最重要的一个应用实例就是经典的Web交互模型中的“一个请求对应一个服务器线程(Thread-per-Request)”的处理方式,这种处理方式的广泛应用使得很多Web服务器应用都可以使用线程本地存储来解决线程安全问题。


http://www.ppmy.cn/news/83570.html

相关文章

死磕测试10余年,呕心整理出了核心知识点已经做成PDF,无私奉献

前言 想在面试、工作中脱颖而出?想在最短的时间内快速掌握软件测试的核心基础知识点?想要成为一位优秀的软件测试工程师?本篇文章能助你一臂之力! 目前正值招聘求职旺季,很多同学对一些新技术名词都能侃侃而谈&#…

finallshell mac SSH工具

一、FinallShell 是什么 FinalShell是一体化的的服务器,网络管理软件,不仅是ssh客户端,还是功能强大的开发,运维工具,充分满足开发,运维需求. 特色功能: 云端同步,免费海外服务器远程桌面加速,ssh加速,本地化命令输入框,支持自动补全,命令历史,自定义命令参数 二、主要特性 …

Java中的Hash碰撞是什么?该如何解决?

在Java中,哈希碰撞(Hash Collision)是指不同的输入数据产生了相同的哈希值。哈希函数是将输入映射到固定大小的哈希值的函数,而碰撞指的是两个不同的输入映射到了相同的哈希值。 哈希碰撞可能导致哈希表、哈希集合或哈希映射等数据结构的性能下降。当两个…

使用U盘给笔记本安装Deepin桌面版(Ubuntu同理)

一、镜像下载 Deepin官网官方ISO镜像源 下载完成后会得到一个 deepin-desktop-community-20.9-amd64.iso 的文件 这个文件就是我们要安装的Deepin的系统镜像文件 二、制作USB启动盘 准备一张至少8G的U盘 或者 内存卡读卡器 这里我选择使用 Rufus 制作USB启动盘 Rufus官网…

Tomcat配置https协议证书-阿里云,Nginx配置https协议证书-阿里云,Tomcat配置https证书pfx转jks

Tomcat/Nginx配置https协议证书 前言Tomcat配置https协议证书-阿里云方式一 pfx配置证书重启即可 方式二 jkspfx生成jks配置证书重启即可 Nginx配置https协议证书-阿里云实现方式重启即可 其他Tomcat相关配置例子如下nginx配置相关例子如下 前言 阿里云官网:https:…

Java高并发核心编程(JUC)—线程详细笔记

进程 线程 线程方法 线程原理 线程状态 查看线程 进程 概述 进程:程序是静止的,进程实体的运行过程就是进程,是系统进行资源分配的基本单位. 一般来说,一个进程由程序段(包含代码、指令集合)、数据段(进程…

设计模式思想

单例模式 懒汉式 线程安全、double check 加锁优化(双重检查)、 jit(编译器)、cpu有可能回对指令重新排序、导致使用未初始化实例、可以通过volatile(wo tai ao)进行修饰,可以防止指令重排 饿汉…

Android应用程序架构分析和基本语法

文章目录 一、控制层与表现层二、Android程序的组成结构三、Android语法基础数据类型与转换转义字符类与对象接口 一、控制层与表现层 在Android应用程序中,逻辑控制层与表现层是分开的设计的。逻辑控制层由Java应用程序实现,表现层由XML文档描述&#…