前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >线程安全&Java内存模型

线程安全&Java内存模型

作者头像
Noneplus
发布2020-08-11 10:22:06
4650
发布2020-08-11 10:22:06
举报
文章被收录于专栏:开发笔记开发笔记

Java内存模型

Java内存模型(JMM)主要目标是定义多线程的情况下线程访问变量的规则

JMM规定线程之间的共享变量存储在主内存中,每个线程都有一个本地内存(工作内存),本地内存存储了共享变量的副本。

aHR0cDovL2ltZy5ibG9nLmNzZG4ubmV0LzIwMTYwOTIxMTgyMzM3OTA0
aHR0cDovL2ltZy5ibG9nLmNzZG4ubmV0LzIwMTYwOTIxMTgyMzM3OTA0

关于线程安全

  • 什么是线程安全问题? 当多个线程同时共享同一个全局变量的操作时候,可能会受到其他线程的干扰,导致数据脏读。(数据一致性问题)
  • 如何解决线程安全问题? 核心思想:在同一时刻,只能有一个线程执行。 通过加锁使线程更加安全,也使程序的执行效率更低。
  • 衡量线程安全的3个要素:
    • 原子性:一个操作或者多个操作要么全部执行,要么都不执行
    • 可见性:多个线程访问同一变量,一个线程修改了变量的值,其他线程可以立即看到修改的值
    • 有序性:程序按照代码的顺序先后执行(与指令重排有关)

Volatile关键字

volatile是一种轻量级的同步机制,可以保证可见性【及时将修改的变量刷新到主内存中】,但不能保证原子性,并且禁止重排序。

volatile在多线程下的适用场景:一写多读

volatile如何保证内存可见性?

当一个线程对volatile修饰的变量进行写操作时,该线程中的本地内存的变量会被立刻刷新到主内存中。

当一个线程对volatile修饰的变量进行读操作时,该线程直接读取主内存的变量。

volatile能否保证线程安全?

不能,保证线程安全需要同时具备原子性,可见性和有序性。而volatile只能保证可见性和有序性,无法保证原子性。

Synchronized关键字

核心思想:在多线程执行同一个方法时,只有获取到锁,才能进入方法里面执行

使用方式:

  • 修饰一个类:其作用的范围是synchronized后面括号括起来的部分,作用的对象是这个类的所有对象
  • 修饰一个方法:被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象
  • 修饰一个静态的方法:其作用的范围是整个方法,作用的对象是这个类的所有对象
  • 修饰一个代码块:被修饰的代码块称为同步语句块,其作用范围是大括号{}括起来的代码块,作用的对象是调用这个代码块的对象

锁的分类

轻量级锁&重量级锁

轻量级锁:手动上锁解锁,扩展性强。代表:Lock

重量级锁:自动上锁解锁,封装程度高。代表:Synchronized

可重入锁&不可重入锁

可重入锁(递归锁):当一个线程已经获取到锁后,再次请求该锁,就可直接获取。(锁的传递,锁的嵌套)代表:Synchronized,Lock

锁的可重入性避免了大部分死锁情况的产生

不可重入锁:不具备传递性

读写锁

ReentrantReadWriteLock

相对Synchronized效率更高,但在多线程情况下,只支持读读共存,不支持读写,写写。

乐观锁与悲观锁

乐观锁(适合多读场景)
  • 思想:认为不会发生线程冲突(本质上是没有锁的)
  • 执行流程,先读取数据,然后在更新前检查在读取至更新这段时间数据是否被修改
    • 未修改:直接更新数据
    • 已修改:重新读取,再次提交更新(或者放弃操作)

为什么乐观锁适合多读场景?

乐观锁是一种更新前的检查机制,相对于悲观锁来说在多读场景下可以减少锁的性能开销,对于多写场景,乐观锁会一直进入已修改,重新读取,再次提交的循环,反而带来更多的资源消耗。

悲观锁(适合多写场景)
  • 思想:认为一定会发生线程冲突
  • 执行流程:读取数据的时候上锁(其他用户无法读取),直到本次数据更新完成才会释放锁。在多写场景下,能保证较高的数据一致性。

【总的来说,乐观锁回滚重试,悲观锁阻塞事务】

CAS无锁机制

原子类:AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference可保证线程安全,底层使用CAS无锁机制

CAS:Compare and Swap,比较再交换,属于乐观锁的一种

  • CAS原理 CAS包含3个参数,CAS(V,E,N),V:主内存的变量值,E:本地内存修改前的值,N:本地内存修改后的值 比较主内存的值和本地内存修改前的值是否一致,若一致,将修改后的值刷新到主内存,若不一致,当前线程放弃更新,将主内存数据刷新到本地内存,再次重试。
  • 优点:非阻塞,不会发生死锁情况,效率更高
  • 缺点:ABA问题(可以通过加入版本号来区分变量是否被修改)
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-08-07 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Java内存模型
  • 关于线程安全
  • Volatile关键字
  • Synchronized关键字
  • 锁的分类
    • 轻量级锁&重量级锁
      • 可重入锁&不可重入锁
        • 读写锁
          • 乐观锁与悲观锁
            • 乐观锁(适合多读场景)
            • 悲观锁(适合多写场景)
          • CAS无锁机制
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档