Java线程安全

Java 线程安全

什么是线程安全?

当多个线程访问某个类时,这个类始终都能表现出正确的行为,那么就称这个类是线程安全的。

一:基本概念

  • 共享资源:能够被多个线程同时访问的资源
  • 竞态条件:当两个线程竞争统一资源时,如果对资源的访问顺序敏感,就称存在静态条件
  • 临界区:导致竞态条件发生的代码区

原子性

一个操作(包含多个子操作)要么全部执行,要么全部不执行。
例如,银行转账,A转给B 1000元,那么需要执行A-1000,B+1000,这操作必须是原子的

可见性
当多个线程并发访问共享变量时,一个线程对共享变量的修改,其它线程能立刻看到。

由于CPU和内存之前会有几层缓存,所以会涉及到缓存更新算法,由操作系统或硬件层面支持。

顺序性

顺序性指的是,程序执行的顺序按照代码的先后顺序执行。

这里就涉及到JVM为了提高性能,会对部分Java代码进行重排序,所以程序执行的顺序不一定会按照代码的先后顺序执行。
但是JVM会保证在单线程情况下,程序最终的执行结果和代码顺序执行一致。

二:线程安全级别

1.不可变
像String、Integer、Long这些,都是final类型的类,任何一个线程都改变不了它们的值,要改变除非新创建一个,因此这些不可变对象不需要任何同步手段就可以直接在多线程环境下使用

2.绝对线程安全
不管运行时环境如何,调用者都不需要额外的同步措施。要做到这一点通常需要付出许多额外的代价,Java中标注自己是线程安全的类,实际上绝大多数都不是线程安全的,不过绝对线程安全的类,Java中也有,比方说CopyOnWriteArrayList、CopyOnWriteArraySet

3.相对线程安全

相对线程安全也就是我们通常意义上所说的线程安全,像Vector这种,add、remove方法都是原子操作,不会被打断,但也仅限于此,如果有个线程在遍历某个Vector、有个线程同时在add这个Vector,99%的情况下都会出现ConcurrentModificationException,也就是fail-fast机制。

4.线程非安全
ArrayList、LinkedList、HashMap等都是线程非安全的类

三:线程安全及解决方案

可能会存在线程安全的点:

  1. 对可变共享资源的操作
  2. 当前的锁对象

例如:

public class Counter {protected long count = 0;public void add(long value){this.count = this.count + value;}
}

如果有两个线程A、B同时对Counter类的同一个实例上执行add方法,A、B执行顺序是不可控的。

  1. 从内存中读取count值到CPU缓存
  2. CPU执行+value操作,并赋值给count
  3. 写回count值到内存

由于上述过程并不是原子性的,所以A、B线程交叉执行时,很可能就会出问题。

Java如何保证原子性

锁和同步

用来保证Java操作的原子性的方式是锁和同步。
使用锁,能够保证同一时间只有一个线程能拿到锁,也就保证了同一时间只有一个线程能执行锁所保护的代码。
使用同步(synchronized):

  1. synchronized修饰静态方法,锁的是该类的Class对象
  2. synchronized修饰非静态方法,锁的是该类的对象实例
  3. synchronized

CAS(Compare and swap)
基础类型变量自增(i++)虽然代码只有一行,但是并不是原子操作。Java中提供了对应的原子操作类来实现该操作,并保证原子性,其本质是利用了CPU级别的CAS指令。由于是CPU级别的指令,其开销比需要操作系统参与的锁的开销小。AtomicInteger使用方法如下:

public static void main(String[] args) throws InterruptedException {final AtomicInteger atomicInteger = new AtomicInteger(0);int count =1000;final CountDownLatch countDownLatch = new CountDownLatch(count);for(int i=0;i<count;i++) {Thread thread = new Thread(new Runnable() {public void run() {atomicInteger.incrementAndGet();countDownLatch.countDown();}});thread.start();}//等待所有线程执行完成countDownLatch.await();//此处输出必等于countSystem.out.println(atomicInteger.get());}

Java如何保证可见性

Java提供了volatile关键字来保证可见性。当使用volatile修饰某个变量时,它会保证对该变量的修改会立即更新到内存中,并且将其他缓存中对该变量的缓存置为失效。因此,其他线程读取该值时,只能从主内存中读取,从而保证可见性。

Java如何保证顺序性

上文讲过编译器和处理器对指令进行重新排序时,会保证重新排序后的执行结果和代码顺序执行的结果一致,所以重新排序过程并不会影响单线程程序的执行,却可能影响多线程程序并发执行的正确性。

Java中可通过volatile在一定程序上保证顺序性,另外还可以通过synchronized和锁来保证顺序性。

synchronized和锁保证顺序性的原理和保证原子性一样,都是通过保证同一时间只会有一个线程执行目标代码段来实现的。

除了从应用层面保证目标代码段执行的顺序性外,JVM还通过被称为happens-before原则隐式地保证顺序性。两个操作的执行顺序只要可以通过happens-before推导出来,则JVM会保证其顺序性,反之JVM对其顺序性不作任何保证,可对其进行任意必要的重新排序以获取高效率。

happens-before原则(先行发生原则)

  • 传递规则:如果操作1在操作2前面,而操作2在操作3前面,则操作1肯定会在操作3前发生。该规则说明了happens-before原则具有传递性
  • 锁定规则:一个unlock操作肯定会在后面对同一个锁的lock操作前发生。这个很好理解,锁只有被释放了才会被再次获取
  • volatile变量规则:对一个被volatile修饰的写操作先发生于后面对该变量的读操作
  • 程序次序规则:一个线程内,按照代码顺序执行
  • 线程启动规则:Thread对象的start()方法先发生于此线程的其它动作
  • 线程终结原则:线程的终止检测后发生于线程中其它的所有操作
  • 线程中断规则: 对线程interrupt()方法的调用先发生于对该中断异常的获取
  • 对象终结规则:一个对象构造先于它的finalize发生

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

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

相关文章

P4899 [IOI2018] werewolf 狼人(kruskal 重构树 + 主席树)

P4899 [IOI2018] werewolf 狼人 给定一个有nnn个点mmm条边的无向图&#xff0c;有QQQ个询问 每次输入S,E,L,RS, E, L, RS,E,L,R&#xff0c;表示你在SSS点出发&#xff0c;要到EEE点&#xff0c;且初始时你是人形态&#xff0c;你只能走[L,n][L, n][L,n]的点&#xff0c; 但…

微软+开源,那些亲爱的以及热爱的

微软 Reactor 社区空间开幕式暨 Azure Meetup 社区活动已于9月7日在上海圆满结束&#xff01;但是…如何构建一个可持续发展的社区未来的路&#xff0c;仍然很长...你应该知道的微软 Reactor微软 Reactor 是微软为构建开发者社区而提供的一个社区空间&#xff0c;以“予力多元…

Java线程问题问答

1、多线程的作用&#xff1f; 发挥多核CPU的优势&#xff0c;提高效率防止阻塞便于建模&#xff1a;将一个任务拆分成多个子任务&#xff0c;分别建立程序模型 2&#xff1a;平时项目中使用锁和synchronized比较多&#xff0c;而很少使用volatile&#xff0c;难道就没有保证可见…

ASP.NET Core 2.2 项目升级至 3.0 备忘录

.NET Core 3.0及ASP.NET Core 3.0 前瞻ASP.NET Core 3.0 迁移避坑指南将 ASP.NET Core 2.2 迁移至 ASP.NET Core 3.0 需要注意的地方记录在这篇随笔中。TargetFramework 改为 netcoreapp3.0 <TargetFramework>netcoreapp3.0</TargetFramework>从 Web 项目&#xff…

Java 死锁

目录&#xff1a; 什么是死锁&#xff1f;死锁是怎么产生的&#xff1f;怎么排查死锁&#xff1f;死锁的预防拓展&#xff1a;Java CPU 100%排查 一 什么是死锁&#xff1f; 注&#xff1a;线程和进程都可能会产生死锁&#xff0c;以下以线程为例 死锁是指两个或两个以上的…

CF196E Opening Portals

CF196E Opening Portals 给定一个有nnn个节点&#xff0c;mmm条边的无向联通图&#xff0c;有kkk个点有portalsportalsportals&#xff0c;当经过了某个点&#xff0c;如果这个点有portalportalportal&#xff0c;它就会永久开启&#xff0c; 对于任意两个开启的portalportal…

.NET Core 微信小程序退款——(统一退款)

点击上方“dotNET名人堂”&#xff0c;选择“设为星标”用学习的姿态&#xff0c;步入工作的状态继上一篇".NET Core 微信小程序支付——&#xff08;统一下单&#xff09;后"&#xff0c;本文将实现统一退款功能&#xff0c;能支付就应该能退款嘛&#xff0c;一般涉…

Java 锁之 CAS

什么是CAS(compare and swap)&#xff1f; CAS&#xff08;Compare & Set&#xff0c;或是 Compare & Swap&#xff09;&#xff0c;即比较并交换&#xff0c;也是实现我们平时所说的自旋锁或乐观锁的核心操作。 它的实现很简单&#xff0c;就是用一个预期的值和内存…

牛客练习赛71 F 红蓝图(kruskal重构树)

红蓝图 给定两个参数x,tx, tx,t&#xff0c;删除边权大于ttt的红边&#xff0c;和边权小于ttt的蓝边&#xff0c;问对于所有的点yyy&#xff0c;既能通过红边走向xxx&#xff0c;又能通过蓝边走向xxx&#xff0c;的点有多少个。 考虑对红边按照边权升序建立一颗kruskalkruska…

.NET 分布式自增Id组件(解决自动分配机器Id、时间回拨问题)

IdHelper是一个.NET&#xff08;支持.NET45或.NET Standard2&#xff09;生成分布式趋势自增Id组件&#xff0c;有两个版本&#xff1a;原始版为基于雪花Id&#xff08;不了解请自行百度&#xff09;方案&#xff0c;需要手动管理设置WorkerId&#xff1b;完美版在原始版的基础…

悲观锁与乐观锁

悲观锁 总是假设最坏的情况&#xff0c;每次取数据的时候都认为别人会来修改&#xff0c;所以每次取数据的时候都会上锁。其它线程想要取这份数据就必须拿到相应的锁&#xff08;共享资源每次只供一个线程使用&#xff0c;其它线程阻塞&#xff0c;用完之后转让给其他线程&…

1190 最小公倍数之和 V2

1190 最小公倍数之和 V2 ∑iablcm(i,b)∑iabibgcd⁡(i,b)b∑d∣b∑i⌈ad⌉bdi[gcd(i,bd)1]b∑d∣b∑k∣bdμ(k)k∑i⌈⌈ad⌉k⌉abkib∑T∣n∑i⌈aT⌉bTi∑k∣Tμ(k)kb∑T∣n(bT⌈aT⌉)(bT−⌈aT⌉1)2∑k∣Tμ(k)k设f(n)∑d∣nμ(d)d,f(1)1,f(p)1−p,f(pk)1−p,且为积性函数\sum…

有关 VS Code 的五大谣言,背后的真相到底是如何的?

2015 年 4 月 29 日&#xff0c;在微软 Build 2015 大会上&#xff0c;微软发布了 Visual Studio Code 第一个预览版本。随着 Visual Studio Code 有了越来越多的使用者&#xff0c;随之而来的各类谣言也层出不穷。让我们就来看看有哪些与 VS Code 相关的谣言&#xff0c;背后的…

Mysql数据库锁机制

一&#xff1a;概念介绍 MySQL数据库锁管理机制&#xff1a; SQL层实现的锁机制    Meta-data元数据锁&#xff1a;在table cache缓存里实现的&#xff0c;为DDL&#xff08;Data Definition Language&#xff09;提供隔离操作。一种特别的meta-data元数据类型&#xff0c;…

HDU 6340 Problem I. Delightful Formulas(伯努利数 + 积性函数反演)

Problem I. Delightful Formulas 大概就是照着题解抄了一遍吧&#xff0c;这道题太神仙了…… aiik,si∑j1iajcalc∑i1nsi[gcd⁡(i,n)1]∑d∣nμ(d)∑i1ndsida_i i ^ k, s_i \sum_{j 1} ^{i} a_j\\ calc\ \sum_{i 1} ^{n} s_i[\gcd(i, n) 1]\\ \sum_{d \mid n} \mu(d) \s…

干货|亲测有效的N倍学习效果笔记法

这里是Z哥的个人公众号每周五11&#xff1a;45 按时送达当然了&#xff0c;也会时不时加个餐&#xff5e;我的第「108」篇原创敬上大家好&#xff0c;我是Z哥。先祝大家中秋快乐。我猜你现在心情不错&#xff0c;毕竟小长假的第一天才开始&#xff0c;后面还有60个小时的假期&a…

Java偏向锁、轻量级锁、重量级锁

先Mark&#xff0c;后补充 参照&#xff1a; https://www.infoq.cn/article/java-se-16-synchronized http://www.cnblogs.com/paddix/p/5405678.html http://www.cnblogs.com/lzh-blogs/p/7477157.html

.NET Core 3.0 可卸载程序集原理简析

文章转载授权级别&#xff1a;A 预计阅读时间&#xff1a;8分钟 损失发量&#xff1a;不好统计因为最近在群里被问到如何理解 .NET Core 3.0 可卸载程序集&#xff0c;所以就写了这篇简单的分析。因为时间实在很少&#xff0c;这篇文章只简单的罗列了相关的代码&…

P4331 [BalticOI 2004]Sequence 数字序列(左偏树)

P4331 [BalticOI 2004]Sequence 数字序列 给定一个序列整数a1,a2,a3,…,an−1,ana_1, a_2, a_3, \dots, a_{n - 1}, a_na1​,a2​,a3​,…,an−1​,an​&#xff0c;要找一个整数序列bbb&#xff0c;满足b1<b2<b3<⋯<bn−1<bnb_1 < b_2 < b_3< \dots&…

.NetCore技术研究-ConfigurationManager在单元测试下的坑

最近在将原有代码迁移.NET Core, 代码的迁移基本很快&#xff0c;当然也遇到了不少坑&#xff0c;重构了不少&#xff0c;后续逐步总结分享给大家。今天总结分享一下ConfigurationManager遇到的一个问题。先说一下场景&#xff1a;迁移.NET Core后&#xff0c;已有的配置文件&a…