第2章-Java并发机制的底层实现原理

本章我们主要学习volatile轻量级锁、synchronized关键字及原子操作的原理。

volatile的应用

概念:volatile是一种轻量级的synchronized(锁)。
特点:

  • 保证可见性
  • 不保证原子性
  • 禁止指令重排
[案例一]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/**
* @desc volatile可见性、非原子性验证
* @Author xw
* @Date 2019/8/19
*/
public class VolatileDemo {
public static void main(String[] args) {
// 验证可见性
seeOkVisibility();
// 验证非原子性
seeNonAtomic();
}
private static void seeNonAtomic() {
MyData myData = new MyData();
for (int i = 1; i <= 100; i++) {
new Thread(() -> {
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
for (int j = 1; j <= 100 ; j++) {
myData.addPlusPlus();
}
}, "T" + i).start();
}
while (Thread.activeCount() > 2) {
}
System.out.println(Thread.currentThread().getName() + "\t mission is over value:" + myData.number);
}
// 设置可见性
private static void seeOkVisibility() {
MyData myData = new MyData();
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t come in");
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
myData.addTO60();
System.out.println(Thread.currentThread().getName() + "\t updated number value: " + myData.number);
}, "AAA").start();
while (myData.number == 0) {
}
System.out.println(Thread.currentThread().getName() + "\t mission is over");
}
}
class MyData {
volatile int number;
public void addTO60() {
this.number = 60;
}
public void addPlusPlus() {
this.number ++;
}
}
[案例二]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* @desc DCL单例 + volatile(禁止指令重排)
* @Author xw
* @Date 2019/8/20
*/
public class SingletonDemo {
private static volatile SingletonDemo instance = null;
private SingletonDemo() {
System.out.println(Thread.currentThread().getName() + "\t 我是构造方法");
}
public static SingletonDemo getInstance() {
if (instance == null) {
synchronized (SingletonDemo.class) {
if (instance == null) {
instance = new SingletonDemo();
}
}
}
return instance;
}
public static void main(String[] args) {
for (int i = 1; i < 100; i++) {
new Thread(() -> {
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
SingletonDemo.getInstance();
}, "T" + i).start();
}
}
}

synchronized的实现原理与应用

概念:锁
三种表现形式:

  • 对于普通同步方法,锁是当前实例对象。
  • 对于静态同步方法,锁是当前类的Class对象。
  • 对于同步方法块,锁是Synchonized括号里配置的对象。

Java对象头

synchronized用的锁是存在Java对象头里的。
Alt text

锁的升级与对比

Java SE 1.6为了减少获得锁和释放锁带来的性能消耗,引入了“偏向锁”和“轻量级锁”,在
Java SE 1.6中,锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状
态和重量级锁状态,这几个状态会随着竞争情况逐渐升级。锁可以升级但不能降级,意味着偏
向锁升级成轻量级锁后不能降级成偏向锁。
Alt text

原子操作的实现原理

概念
原子:不能被进一步分割的最小粒子。
原子操作:不可被中断的一个或一系列操作。

Java如何实现原子操作

使用循环CAS实现原子操作

  • 存在几个问题
    • ABA问题
    • 循环时间长开销大
    • 只能保证一个共享变量的原子操作
      [代码]
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      /**
      * @desc CAS保证原子性
      * T1 100 -> 101
      * T2 100 -> 2019
      * @Author xw
      * @Date 2019/8/20
      */
      public class CasDemo {
      public static void main(String[] args) {
      AtomicInteger atomicInteger = new AtomicInteger(100);
      // T1 100 -> 101
      new Thread(() -> {
      System.out.println(Thread.currentThread().getName() + "\t updated: " + atomicInteger.compareAndSet(100, 101) + ",value: " + atomicInteger.get());
      }, "T1").start();
      // T2 100 -> 2019
      new Thread(() -> {
      try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
      System.out.println(Thread.currentThread().getName() + "\t updated: " + atomicInteger.compareAndSet(100, 2019) + ",value: " + atomicInteger.get());
      }, "T2").start();
      }
      }

使用锁机制实现原子操作

锁机制保证了只有获得锁的线程才能够操作锁定的内存区域。

[代码]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
private static void atomicReferenceABA() {
new Thread(() -> {
atomicReference.compareAndSet(100, 101);
atomicReference.compareAndSet(101, 100);
}, "t1").start();
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicReference.compareAndSet(100, 2019) + "\t" + atomicReference.get());
}, "t2").start();
}
public static void main(String[] args) {
// 基础版
atomicReferenceABA();
}