Java并发 - (并发基础)

1、什么是共享资源

  • 堆是被所有线程共享的一块内存区域。在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例

  • Java中几乎所有的对象实例都在这里分配内存。方法区与堆一样,也是各个线程共享的一块内存区域,它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。

  • 光看文字,会让我们觉得很抽象。如下图:

2、并发编程的难点

原子性问题

  • 操作系统做任务切换(CPU切换),可以发生在任何一条CPU指令执行完成后;
  • CPU能保证的原子操作是指令级别的,而不是高级语言的操作符(例如:n++)。
  • 如下图,是n++编译后,被编译CPU执行的指令。

可见性问题

  • 可见性是指一个线程对共享变量的修改,另外一个线程能够立刻看到。
  • 可见性问题是由CPU的缓存导致的,多核CPU均有各自的缓存,这些缓存均要与内存进行同步。

有序性问题

  • 在执行程序时。为了提高性能,编译器和处理器常常会对指令做重排序;
  • 重排序不会影响单线程的执行结果,但是在并发情况下,可能会出现诡异的BUG。
  • 参考地址:https://zhuanlan.zhihu.com/p/298448987

3、JMM

并发编程的关键目标

并发编程需要处理两个关键问题,即线程之间如何通信和同步。

  • 通信:指线程之间以何种机制来交换信息;
  • 同步:指程序中用于控制不同线程之间的操作发生的相对顺序的机制。

并发编程的内存模型

共有两种并发编程模型:共享内存模型、消息传递模型,Java采用的是前者。

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

❣️ 这个内存模型:JMM

JMM 是Java Memory Model的缩写,Java线程之间的通信由 JMM 控制,即 JMM决定一个线程对共享变量的写入何时对另一个线程可见。JMM定义了线程和主内存之间的抽象关系,通过控制主内存与每个本地内存(抽象概念)之间的交互,JMM为Java程序员提供了内存可见性的保证。

源代码与指令间的重排序

为了提高性能,编译器和处理器常常会对指令做重排序。重排序有3种类型,其中后2种都是处理器重排序。这些重排序可能会导致多线程程序出现内存可见性问题。

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

重排序对可见性的影响

参考下图,虽然处理器执行的顺序是A1->A2,但是从内存角度来看,实际发生的顺序是A2->A1。这里的关键是,由于写缓冲区仅对自己的处理器可见,它会导致处理器执行内存操作的顺序可能会与实际的操作执行顺序不一致。由于现代的处理器都会使用写缓冲区,因此它们都会允许对写 - 读操作执行重排序。

如何解决重排序带来的问题

对于编译器,JMM的编译器重排序规则会禁止特定类型的编译器重排序(比如volatile)。

对于处理器重排序,JMM的处理器重排序规则会要求编译器在生成指令序列时,插入特定类型的内存屏障(Memcry Barries /Memory Fence)指令,通过内存屏障指令来禁止特定类型的处理器重排序。

由于常见的处理器内存模型比JMM要弱, Java编译器在生成字节码时,会在执行指令序列的适当位置插入内存屏障来限制处理器的重排序。同时,由于各种处理器内存模型的强弱不同,为了在不同的处理器平台向程序员展示一个一致的内存模型,JMM在不同的处理器中需要插入的内存屏障的数量和种类也不同。

CPU内存屏障:

  • LoadLoad: 禁止读和读的重排序;
  • StoreStore:禁止写和写的重排序;
  • LoadStore:禁止读和写的重排序;
  • StoreLoad:禁止写和读的重排序。

Java内存屏障

public final class Unsafe {
public native void loadFerice();// LoadLoad + LoadStore
public native void storeFence();// StoreStore + LoadStore
public native void fullFence(); // loadFence() + storeFence() + StoreLoad
}

happens-before

JMM使用happens-before规则来阐述操作之间的内存可见性,以及什么时候不能重排序。在JMM中。如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须要存在happens-before关系。换个角度来说.如果A happens-before B,则意味着A的执行结果必须对B可见,也就是保证跨线程的内存可见性。其中,前4条规则与程序员密切相关。

  • 1、程序顺序规则:一个线程中的每个操作, happens-before于该线程中的任意后续操作;
  • 2、volatile变量规则:对一个volatile域的写, happens-before于任意后续对这个volatile域的读;
  • 3、synchronized规则:对一个锁的解锁, happens-before于随后对这个锁的加锁;
  • 4、传递性:若A happens-before B,且B happens-before C,则A happens-before C;
  • 5、start()规则:若线程A执行Thread.start(),则线程A的start()操作happens-before于线程B中的任意操作;
  • 6、 join()规则︰若线程A执行ThreadB.join()并成功返回,那么线程B中的任意操作happens-before于线程A从ThreadB.join()的成功返回。

4、Volatile

volatile的基本特性

  • 可见性:对一个volatile变量的读,总是能看到对这个volatile变量最后的写入;
  • 原子性:对任意单个volatile变量的读/写具有原子性,但类似vclatile++这种复合操作不具有原子性。

volatile的内存语义

  • 写内存语义:当写一个volatile变量时,JMM会把该线程本地内存中的共享变量的值刷新到主内存;
  • 读内存语义:当读一个volatile变量时,JMM会把该线程本地内存置为无效,使其从主内存中读取共享变量。

volatile的实现机制

为了实现volatile的内存语义,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。内存屏障插入策略非常保守,但它可以保证在任意处理器平台,任意的程序中都能得到正确的volatile内存语义。

  • 在每个volatile写操作的前面插入一个StoreStore屏障;
  • 在每个volatile写操作的后面插入一个StoreLoad屏障;
  • 在每个volatile读操作的后面插入—个LoadLoad屏障;
  • 在每个volatile读操作的后面插入一个LoadStore屏障。

volatile与锁的对比

volatile仅仅保证对单个volatile变量的读/写具有原子性,而锁的互斥执行的特性可以确保对整个临界区代码的执行具有原子性。在功能上锁比volatile更强大,在可伸缩性和执行性能上volatile更有优势。

5、锁

锁的内存语义

  • 当线程释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中;
  • 当线程获取锁时,JMM会把该线程对应的本地内存置为无效。

锁的实现机制

  • synchronized:采用CAS + Mark Word实现。存在锁升级的情况;
  • Lock:采用CAS + volatile实现。存在锁降级的情况核心是AQS 。

😀 Java并发 - (并发基础)的更多相关文章

  1. Java并发编程基础

    Java并发编程基础 1. 并发 1.1. 什么是并发? 并发是一种能并行运行多个程序或并行运行一个程序中多个部分的能力.如果程序中一个耗时的任务能以异步或并行的方式运行,那么整个程序的吞吐量和可交互 ...

  2. 并发-Java并发编程基础

    Java并发编程基础 并发 在计算机科学中,并发是指将一个程序,算法划分为若干个逻辑组成部分,这些部分可以以任何顺序进行执行,但与最终顺序执行的结果一致.并发可以在多核操作系统上显著的提高程序运行速度 ...

  3. Java并发编程系列-(1) 并发编程基础

    1.并发编程基础 1.1 基本概念 CPU核心与线程数关系 Java中通过多线程的手段来实现并发,对于单处理器机器上来讲,宏观上的多线程并行执行是通过CPU的调度来实现的,微观上CPU在某个时刻只会运 ...

  4. Java并发(基础知识)—— Executor框架及线程池

    在Java并发(基础知识)—— 创建.运行以及停止一个线程中讲解了两种创建线程的方式:直接继承Thread类以及实现Runnable接口并赋给Thread,这两种创建线程的方式在线程比较少的时候是没有 ...

  5. 多线程(一)java并发编程基础知识

    线程的应用 如何应用多线程 在 Java 中,有多种方式来实现多线程.继承 Thread 类.实现 Runnable 接口.使用 ExecutorService.Callable.Future 实现带 ...

  6. Java并发编程基础三板斧之Semaphore

    引言 最近可以进行个税申报了,还没有申报的同学可以赶紧去试试哦.不过我反正是从上午到下午一直都没有成功的进行申报,一进行申报 就返回"当前访问人数过多,请稍后再试".为什么有些人就 ...

  7. Java高并发编程基础三大利器之CountDownLatch

    引言 上一篇文章我们介绍了AQS的信号量Semaphore<Java高并发编程基础三大利器之Semaphore>,接下来应该轮到CountDownLatch了. 什么是CountDownL ...

  8. Java并发编程--基础进阶高级(完结)

    Java并发编程--基础进阶高级完整笔记. 这都不知道是第几次刷狂神的JUC并发编程了,从第一次的迷茫到现在比较清晰,算是个大进步了,之前JUC笔记不见了,重新做一套笔记. 参考链接:https:// ...

  9. 【Java虚拟机4】Java内存模型(硬件层面的并发优化基础知识--缓存一致性问题)

    前言 今天学习了Java内存模型第一课的视频,讲了硬件层面的知识,还是和大学时一样,醍醐灌顶.老师讲得太好了. Java内存模型,感觉以前学得比较抽象.很繁杂,抽象. 这次试着系统一点跟着2个老师学习 ...

  10. java多线程与并发(基础篇)

    一.进程与线程 进程:是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位. 线程:是进程的一个执行路径,一个进程中至少有一个线程,进程中的多个线程共享进程的 资源. 虽然系统是把资源 ...

随机推荐

  1. .Net+Vue3实现数据简易导入功能

    在开发的过程中,上传文件或者导入数据是一件很常见的事情,导入数据可以有两种方式: 前端上传文件到后台,后台读取文件内容,进行验证再进行存储 前端读取数据,进行数据验证,然后发送数据到后台进行存储 这两 ...

  2. KingbaseES 全局索引是否因为DDL操作而变为Unusable ?

    前言 Oracle 在对分区做DDL操作时,会使分区全局索引失效,需要加上关键字update global indexes.KingbaseES 同样支持全局索引.那么,如果对分区表进行DDL操作,那 ...

  3. 自定义View5 -塔防小游戏:第二篇防御塔随意放置

    第一篇:一个防御塔+多个野怪(简易版) 第二篇:防御塔随意放置 自定义View,处理事件分发,up,move,down. 第三篇:防御塔随意放置+多组野怪 第四篇:多波野怪 第五篇:杀死野怪获得金币 ...

  4. 微信小程序-坑,wxml里wx:if 判断 数字 是否在一个数组中。

    <view wx:if="{{item.index}} in {{vote_list}}"> 已赞 <image src="/static/zan_y. ...

  5. nginx日志参数及含义

    参数含义 $remote_addr,$http_x_forwarded_for #记录客户端IP地址 $remote_user #记录客户端用户名称 $request #记录请求的URL和HTTP协议 ...

  6. tcp_tw_recycle参数引发的故障

    文章转载自:https://blog.csdn.net/wireless_tech/article/details/6405755 故障描述: 2010年9月7日,新上线的手机游戏论坛有部分地区用户反 ...

  7. 组件化开发2-安装cocoaPods

    第一步:安装ruby 不能一上来就换ruby源.虽然mac自带了ruby,但是版本一般都偏低,如果不进行更新会导致版本依赖问题. 这里使用rvm来管理ruby,它允许共存多个ruby.RVM:Ruby ...

  8. NSIS安装界面无虚线框移动

    最近很多应用程序都在安装界面的美化上面下足了功夫,一个漂亮流畅的安装界面无疑会给其带来用户体验上的加分,其中一个无虚线框跟随鼠标移动比较有趣,狂翻msdn后终于找到了控制函数SystemParamet ...

  9. vue3中pinia的使用总结

    pinia的简介和优势: Pinia是Vue生态里Vuex的代替者,一个全新Vue的状态管理库.在Vue3成为正式版以后,尤雨溪强势推荐的项目就是Pinia.那先来看看Pinia比Vuex好的地方,也 ...

  10. 路径分析—QGIS+PostgreSQL+PostGIS+pgRouting(一)

    前言 因业务需求,需要做最短路径分析.最近几天查询资料,并自己动手,实现了简单的路径分析. 下面就介绍具体的实现过程. 本篇文章最终结果是在 PostgreSQL 数据库中实现的,后续的可视化展示会继 ...