多线程的使用-->5:并发编程的特性

目录

1.并发编程特性:

2.并发编程的特性之一:原子性

3.并发编程的特性之一:可见性

3.1 JVM内存模型

3.2JMM(Java 内存 模型)

4.并发编程的特性之一:有序性


1.并发编程特性:

  • 多线程是一种程序开发或设计环境

  • 并发编程是一种程序设计概念或设计目标,在多线程开发环境中,同一系列的程序设计与机制的使用,确保多线程开发环境是稳定的,快速的,性能优秀的。

  • 并发编程具有3个特征

    • 原子性

    • 可见性

    • 有序性

  • 在多线程开发时,如果程序设计不是特别的完美,有可能会出现原子性问题,可见性问题,有序性问题

  • 我们就会尽可能的解决上述问题,使得我们的程序具有原子性,可见性,有序性。 这样的程序开发就成为并发编程。

2.并发编程的特性之一:原子性

  • 一系列操作或一些列代码指令属于一个独立的单元

  • 在一个线程执行过程中,不受到另一个线程的影响

  • 我们就称之为复合原子性。否则就是不符合原子性。

  • 所以原子性的特点就是:一系列指令要么全都执行完毕,要么全都不执行。

  • 原子性问题如何解决?上锁

    • 悲观锁 : synchronized , lock

    • 乐观锁:CAS + 自旋 。 AtomicInteger

3.并发编程的特性之一:可见性

要想了解可见性,需要先了解 JMM (JAVA 内存 模型) , 需要再先回忆JVM内存模型(运行时数据区)

3.1 JVM内存模型

  • jvm在运行过程中,会产生很多的数据(对象,属性,变量等),这些数据都会存储在jvm内存中随着作用及特点不同,会存储在不同的区域

  • 这个区域分为5部分

    1. 方法区

      用来存放类加载的信息,同时存放静态属性 和 方法(静态方法和普通方法)

      jdk1.7之后,取消了方法区名称,改为元空间

      方法区也叫元空间也叫永久区

      方法区中的数据,可以被多线程共享。访问时会有数据共享的安全问题

    2. 堆区

      用来存放对象或数组的 , 也就是用来存放new关键字创建的内容

      堆区中的数据,可以被多线程共享。访问时会有数据共享的安全问题

    3. 方法栈

      用来存放调用方法及方法执行的过程中产生的临时数据的(临时变量,参数列表)

      这些数据是以栈帧结构存入方法栈(压栈)

      随着方法执行完毕,这些数据就会被销毁回收(弹栈)

      栈帧分为:局部变量表, 操作数栈,返回地址,动态链接。

      方法栈是线程独享的,也就是每一个线程都会有一个方法栈。

    4. 程序计数器

      在线程上下文切换时,用来记录刚刚执行的行号

      程序计数器也是线程独享的。

    5. 本地方法栈

      特点与方法栈基本相同

      不同点时,当调用native修饰的方法时,其产生的数据会存储在本地方法栈中。

3.2JMM(Java 内存 模型)

  • JMM不是一个具体的内容,也不是对内存的一个划分,JMM是一个规则规范

  • JVM开发人员,按照这个规范,来开发JVM,使其在多线程应用中复合JMM规范

  • JVM使用人员,按照这个规范,来使用JVM,使得可以更好的实现多线程开发。

  • JMM规范

    1. 所有的共享数据,存储在一个主内存中

    2. 每一个线程都有自己的工作内存

    3. 当线程需要使用共享数据时,不能主内存中直接操作

      而是要先从主内存备份到工作内存

      在工作内存中对数据进行修改

      再将工作内存中的数据,更新回主内存。

    4. 每个线程的工作内存彼此之间是不可见的。

  • 基于JMM规范的特点,如果我们在多线程访问共享资源时,不加以设计,有可能出现数据不可见问题

  • 所以我们需要解决多线程(工作内存中共享)数据(备份)不可见问题

  • 如何解决可见性问题?

    • 加锁 ,每次加锁前,都会从主内存备份数据,每次释放锁时,都会向主内存更新数据。

      System.out.println方法底层使用synchronized关键字加了锁

    • 线程睡眠,每次睡醒都会去主内存重新加载数据

    • 将需要可见的变量,使用volatile关键字修饰 (轻量级同步机制)

      线程每次要获得volatile关键字修饰的变量值时,都会从主内存加载新数据

      线程每次为volatile关键字修饰的变量赋值时,都会将新值跟新会主内存。

4.并发编程的特性之一:有序性

  • 在编译和运行过程中,有可能为了优化编译和运行过程,会调整代部分码指令的顺序。

  • 使得在单线程环境中,最终的执行结果不变,称之为 指令重排序

  • 在多线程的环境中,可能就会存在一些问题,称为有序性问题。

  • 如何解决有序性问题?

    • 有序性问题会涉及一个 happen-before原则

    • 只要复合上述原则的程序代码,就不会出现有序性问题 (代表不会出现指令重排序)

      • 加锁可以避免指令重排序

      • 使用volatile关键字修饰属性,在使用该属性时(赋值,取值)。该属性上面和下面的代码不会重排序

        volatile修饰的属性,在jvm执行时,会增加内存屏障

你可能感兴趣的:(java,开发语言)