学习多并发第一课--多并发基础

学习多并发第一课--多并发基础

目录

1.为什么需要并发编程

为了提高运行效率,程序运行会采用多线程运行,当多个线程操作同一个资源时,会涉及到一系列的问题。

2.并发编程需要解决的问题

  • 原子性问题

    操作系统切换任务的时候,可以发生在每一次CPU指令结束 操作系统在运行时,保证的原子性是指令级别的,而不是高级语言JAVA的运算符 例如 i++,是三个指令

  • 可见性问题

    可见性是有CPU缓存导致的,多核CPU运行时,每核CPU都有自己的缓存,在缓存没和内存进行同步时,一个线程对共享资源的改变,是其他线程看不到的.

  • 有序性问题

    在执行程序的时候,CPU会对指令重新排序。重新排序的结果不会影响单线程的运行结果,但是当多线程运行的时候,会产生诡异的BUG

3.怎么解决问题(JMM模型如何解决可见性,有序性问题)

3.1并发编程的关键目标(通信和同步)

  • 通信:是指不同线程之间以何种方式交换信息**(可见性问题)**
  • 同步:是指如何控制不同线程之间的操作发生的相对顺序**(有序性问题)**

3.2 并发编程的内存模型

共有两种内存模型,共享内存模型和消息传递模型,Java才用的是共享内存模型

  • 共享内存模型下,线程之间共享程序的公众状态,采用写-读内存的方式进行隐式通信
  • 共享内存模型下,同步是显示进行的,程序员必须用显示指定代码需要在线程之间互斥执行

3.3 JMM模型

JMM如何解决可见性问题

Java的内存共享模型叫JMM模型, Java线程之间的通信由JMM控制,即JMM决定了一条线程对共享资源的操作何时对另一个线程可见 JMM定义了线程和主内存之间的抽象关系,通过控制主内存与线程的"本地内存"(抽象概念)来保证内存的可见性

JMM如何解决有序性

为了提高性能,编译器和处理器在不影响语义的情况下会对指令重排序,重排序的类型有三种, **1.编译器优化重排序:编译器在不改变单线程程序语义的情况下,会对指令重排序
2.指令级并行重排序:现代处理器采用指令并行技术将多条指令重叠执行,如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序
3.系统内存重排序:**由于处理器使用缓存和读/写缓存区,这使得加载和存储操作看上去可能是在乱序执行

alt 第一种是编译器重排序,后两种是处理器重排序

针对编译器重排序

JMM的编译器重排序规则会禁止特定类型的指令重排序

针对处理器重排序

JMM会在不同处理器插入不同种类和数量的内存屏障

3.4 内存屏障类型(重排序问题)

CPU内存屏障

  1. LoadLoad :禁止读和读的重排序
  2. StoreStore:禁止写和写的重排序
  3. LoadStore:禁止读和写的重排序
  4. StoreLoad:禁止写和读的重排序

JMM内存屏障

有一个Unsafe类下有三个方法,JMM用于插入内存屏障

  • public native void loadFence(); LoadLoad +LoadStore
  • public native void storeFence() StoreStore+LoadStroe
  • public native void fullFence();; loadFence()+StoreFence()+StoreLoad

3.5 happens-before规则(可见性问题)

JMM使用happens-before规则来阐述操作之间的可见性问题,以及什么时候不能重排序 在JMM中,如果一个操作需要对另一个操作可见,两者之间就要有happens-before规则

  1. 程序顺序规则:
  2. synchronized规则:
  3. volatile规则:
  4. 传递性:
  5. statr()规则:
  6. join()规则:

  1. 关键字Volatile

基本特性:

  • 保证内存的可见性
  • 禁止指令重排序

内存语义:

-写内存语义:当写一个volatile变量后,JMM会把该变量刷新到主内存中

-读内存语义:当读一个volatile变量时,会把线程内的本地内存置为无效,从主内存中读取

实现机制:

分别在读写操作的前后插入内存屏障

  • 在volatile读操作之前加入 LoadLoad
  • 在volatile读操作之后计入LoadStore
  • 在volatile写操作之前加入StoreStore
  • 在volatile写操作之后加入StoreLoad

5.锁

内存语义

-在线程拿到锁时 JMM会把本线程的缓存置为无效,去主内存中读取

-在线程释放锁时,JMM会把该线程缓存中的共享变量刷新到主内存中

实现机制:

-synchronized: 采用Mark Work+CAS实现,存在锁升级的情况

-Lock; 采用volatile+CAS实现的,存在锁降级的情况 核心是AQS

synchronized和lock的区别

  1. synchronized不需要手动释放锁,lock需要手动释放锁
  2. synchronized无法获取锁的状态,而lock可以
  3. synchronized 是java内置关键字,而lock是一个类
  4. synchronized无法中断,为非公平锁而lock可以中断,可以设置公平和非公平
  5. synchronized 不同线程只有一个线程可以获取锁,其他线程会陷入阻塞直到获取锁,而lock有阻塞锁也有非阻塞做,阻塞锁还有尝试设置,
  6. synchronized适合锁少量代码块,lock适合锁大量代码块
全部评论

相关推荐

牛客279957775号:铁暗恋
点赞 评论 收藏
分享
头像
11-09 17:30
门头沟学院 Java
TYUT太摆金星:我也是,好几个华为的社招找我了
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务