zoukankan      html  css  js  c++  java
  • 线程安全&Java内存模型

    Java内存模型

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

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

    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问题(可以通过加入版本号来区分变量是否被修改)

  • 相关阅读:
    iptables防火墙操作
    磁盘配置
    软件包安装
    网络配置
    用户、组及权限控制
    常见的Linux操作
    Vmware下CentOS 7安装步骤
    Spark(十四)【SparkSQL集成Hive】
    Scala和Java的List集合互相转换
    Spark(十)【RDD的读取和保存】
  • 原文地址:https://www.cnblogs.com/noneplus/p/13454046.html
Copyright © 2011-2022 走看看