线程安全问题?怎么解决线程安全

2023-05-16

一、线程安全等级

       之前的博客中已有所提及“线程安全”问题,一般我们常说某某类是线程安全的,某某是非线程安全的。其实线程安全并不是一个“非黑即白”单项选择题。按照“线程安全”的安全程度由强到弱来排序,我们可以将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()方法。如果有两个线程同时持有一个线程对象,一个尝试去中断线程,另一个尝试去恢复线程,如果并发进行的话,无论调用时是否进行了同步,目标线程都有死锁风险。正因此如此,这两个方法已经被废弃啦。

二、线程安全的实现方法

        保证线程安全以是否需要同步手段分类,分为同步方案和无需同步方案。

       20180315101631748.

       1、互斥同步

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

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

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

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

       2、非阻塞同步 

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

        非阻塞的实现CAS(compareandswap):CAS指令需要有3个操作数,分别是内存地址(在java中理解为变量的内存地址,用V表示)、旧的预期值(用A表示)和新值(用B表示)。CAS指令执行时,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://my.oschina.net/u/4085644/blog/3020454

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

线程安全问题?怎么解决线程安全 的相关文章

  • ftp服务器无法使用浏览器访问解决方法

    浏览器默认工作在被动模式 可能你的环境无法让ftp服务器工作在主动模式 这个很容易测试 你在IE浏览器的工具 internet选项 高级中取消使用被动ftp的设置 xff0c 看看浏览器是否还可以访问ftp服务器 xff0c 如果访问不了
  • Python抓取糗事百科成人版图片

    最近开始学习爬虫 xff0c 一开始看的是静觅 的爬虫系列文章 xff0c 今天看到糗事百科成人版 xff0c 心里就邪恶了一下 xff0c 把图片都爬下来吧 xff0c 哈哈 虽然后来实现了 xff0c 但还是存在一些问题 xff0c 暂
  • vc中调用Com组件的方法详解

    Requirement 1 创建myCom dll 该COM只有一个组件 两个接口IGetRes 方法Hello IGetResEx 方法HelloEx xff1b 2 在工程中导入组件或类型库 import 34 组件所在目录myCom
  • matlab练习程序(二值图像内外边界跟踪)

    目标内边界的像素全都在目标里面 xff0c 目标外边界的像素全都不在目标上 xff0c 是包围着目标的 二值图像内外边界的计算都是有两种方法的 xff0c 所以一共是4种算法 xff0c 不过实际用到跟踪的只有一个而已 首先是内边界跟踪 x
  • 如何使用.NET开发全版本支持的Outlook插件产品(三)——全面控制

    插件项目所有代码都已经上传至 https github com VanPan TestOutlookAdding 进阶基础 COM查看 首先 xff0c 对于Outlook对象模型 xff0c MSDN早就有非常详细的介绍 xff0c 请直
  • Android中应用程序如何获得系统签名权限

    最近在做一个控制电视界面的应用 xff0c 模拟电视遥控器操作 xff0c 代码如下 public static void simulateKeystroke final int KeyCode new Thread new Runnabl
  • 使用PostSharp在.NET平台上实现AOP

    摘要 本文首先介绍AOP xff08 面向方面编程 xff09 的相关概念及理论 xff0c 然后介绍如何使用PostSharp框架在 NET平台上实现AOP xff0c 最后对PostSharp的机制及AOP的优劣进行一个简单的分析 AO
  • python 带头节点的单链表相关函数

    链表用头结点还是头指针让我混乱 在写append函数时 xff0c 发现网上的写法各有不同 xff0c 而带头结点的会更好理解 xff0c 也更简洁 以下是带头结点的单链表一些简单的相关函数 span class token keyword
  • 使用 Beautiful Soup 解析网页内容

    安装Beautiful Soup Beautiful Soup是一个Python的HTML解析框架 xff0c 我们可以利用它方便的处理HTML和XML文档 Beautiful Soup有3和4两个版本 xff0c 目前3已经停止开发 所以
  • ibm服务器报警指示灯含意

    EVENT LOG指示灯报警 有台IBM服务器前面的光通路面板开始亮起了小黄灯 xff0c 推出这个小盒子一看 xff0c 是EVENT LOG指示灯报警 一时不知道是什么原因 xff0c 可能是日志错误 xff0c 要进Configura
  • [概念学习] Virtualization的几个概念

    1 Vitualization xff1a 某种东西的虚拟版本 xff0c 比如硬件平台 操作系统 存储设备 网络资源等 the creation of a virtual version of something such as a ha
  • Windows2012、windows2016配置多用户登录

    windows系统多用户登录配置方法如下 xff0c 但是120天后还是会提示缺少远程桌面授权服务器 xff0c 根本解决办法 xff0c 请参考 xff1a https blog 51cto com 13777088 2299170 服务
  • 全自动迁移数据库的实现 (Fluent NHibernate, Entity Framework Core)

    在开发涉及到数据库的程序时 xff0c 常会遇到一开始设计的结构不能满足需求需要再添加新字段或新表的情况 xff0c 这时就需要进行数据库迁移 实现数据库迁移有很多种办法 xff0c 从手动管理各个版本的ddl脚本 xff0c 到实现自己的
  • VM virtuaBox异常关机启动不了的解决方案

    事件回放 我的物理机是win7 xff0c 上面装了一个 VM virtualBox 用来装 Centos xff0c 有天物理机非正常关闭 xff0c 导致 VM virtuaBox异常关机启动不 了 xff0c 如下 确实找不到这个vm
  • SpringMVC系列(十一)把后台返回的数据转换成json、文件下载、文件上传

    一 后台返回的数据转换成json 1 引入转换json需要的3个依赖 1 lt json转换需要的依赖 begin gt 2 lt dependency gt 3 lt groupId gt com fasterxml jackson co
  • html 复选框checkbox

    阅读目录 统计选中复选框的个数点击复选框 xff0c 执行相应的函数复选框的状态层次关系 xff0c 选中父节点 xff0c 子节点全部选中 回到顶部 统计选中复选框的个数 lt html gt lt head gt lt title gt
  • android kk界面旋转流程_Android屏幕旋转前180度旋转以正确定向

    由于硬件问题 xff0c 我们必须将我们开发的Android平板电脑上下180度安装 我们设法翻转屏幕回来做以下修改 xff0c 以正确的方向 xff1a Android屏幕旋转前180度旋转以正确定向 框架 基 服务 SurfaceFli
  • 理解 简单的递归实现阶乘过程中的参数传递

    span class token keyword def span span class token function factorial span span class token punctuation span n span clas
  • 程序员工作中的一些建议

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 一 尽早地添加日志记录和错误处理 在开发新系统时 xff0c 我做的第一件事就是添加日志和错误处理 xff0c 因为这两者从一开始就非常有用 如果系统不能照常工作 xff0
  • MongoDB简单介绍以及基本命令

    一 MongoDB简介 1 MongoDB是什么 MongoDB 是一个基于分布式文件存储的数据库 由 C 43 43 语言编写 旨在为 WEB 应用提供可扩展的高性能数据存储解决方案 MongoDB 是一个介于关系数据库和非关系数据库之间

随机推荐