【多线程】基础

558 阅读4分钟

一、并发编程的三个重要特性

1. 原子性

一个的操作或者多次操作,要么所有的操作全部都得到执行并且不会受到任何因素的干扰而中断,要么所有的操作都执行,要么都不执行。

synchronized 可以保证代码片段的原子性。

2. 可见性

当一个变量对共享变量进行了修改,那么另外的线程都是立即可以看到修改后的最新值。

volatile 关键字可以保证共享变量的可见性。

3. 有序性

代码在执行的过程中的先后顺序,Java 在编译器以及运行期间的优化,代码的执行顺序未必就是编写代码时候的顺序。

volatile 关键字可以禁止指令进行重排序优化。

二、Synchronized关键字

1. synchronized关键字

synchronized 关键字解决的是多个线程之间访问资源的同步性,synchronized关键字可以保证被它修饰的方法或者代码块在任意时刻只能有一个线程执行。可以保证原子性、可见性,不能保证有序性,禁止指令重排序。

在早期版本中,synchronized属于 重量级锁效率低,JDK1.6之后对锁实现引入了大量的优化,减少了锁操作的开销

2. synchronized的使用

修饰实例方法、修饰静态方法、修饰代码块,归根结底上锁的资源只有两类:对象、类

synchronized关键字,加到static静态方法和synchronized(class)代码块上都是给Class类上锁;加到实例方法上是给对象实例加锁。

  • 不要使用synchronized(String a)因为JVM中,字符串常量池具有缓存功能
  • 构造方法不能使用 synchronized 关键字修饰,构造方法本身就属于线程安全的,不存在同步的构造方法一说。

3. synchronized的底层原理

底层原理属于JVM层面,两者的本质都是对对象监视器 monitor 的获取。 (1)同步语句块

synchronized 同步语句块的实现使用的是 monitorenter 和 monitorexit 指令,其中 monitorenter 指令指向同步代码块的开始位置,monitorexit 指令则指明同步代码块的结束位置。

  • 当执行 monitorenter 指令时,线程试图获取锁也就是获取 对象监视器 monitor 的持有权(在java虚拟机中,每个对象中都内置了一个ObjectMonitor对象。)
  • 在执行monitorenter时,会尝试获取对象的锁。如果锁的计数器为0则表示锁可以被获取
  • 在执行monitorexit指令后,将锁计数器设为0,表明锁被释放。

(2)同步方法

在flags上有一个ACC_SYNCHRONIZED 标识,该标识指明了该方法是一个同步方法。JVM 通过该 ACC_SYNCHRONIZED 访问标志来辨别一个方法是否声明为同步方法,从而执行相应的同步调用。

4. JVM对synchronized的优化

  • 锁膨胀 偏向锁、轻量级锁、重量级锁
  • 锁消除
  • 锁粗化
  • 自旋锁与自适应自旋锁

三、Volatile关键字

volatile可以保证可见性、顺序性、一致性,保证在多线程环境下也能正常运行。

1. 特性

  • 可见性 volatile修饰的对象在加载时会告知JVM,对象在CPU的缓存上对多个线程是同时可见的。
  • 有序性 禁止指令重排,这里有JVM的内存屏障的概念,简单理解为:可以保证线程操作对象时是顺序执行的。 ——解决了long、double赋值的原子性问题
  • 一致性 可以保证多个线程读取数据时,读取到的数据是最新的。(注意读取的是最新的数据,但不保证写回时不会覆盖其他线程修改的结果)

2. CPU缓存模型与JMM

i. 内存屏障 内存屏障(Memory Barrier)也称为内存栅栏或栅栏指令,是一种CPU屏障指令,它使CPU或编译器对屏障指令之前和之后发出的内存操作执行一个排序约束。 这通常意味着在屏障之前发布的操作被保证在屏障之后发布的操作之前执行。

3. synchronized关键字和volatile关键字的区别

synchronized 关键字和 volatile 关键字是两个互补的存在

  • volatile 关键字是线程同步的轻量级实现,所以volatile 性能肯定比 synchronized 关键字要好。但是volatile 关键字只能用于变量而 synchronized 关键字可以修饰方法以及代码块。
  • volatile 关键字能保证数据的可见性,但不能保证数据的原子性。synchronized 关键字两者都能保证。 volatile 关键字主要用于解决变量在多个线程之间的可见性,而 synchronized 关键字解决的是多个线程之间访问资源的同步性。