第一部分:并发理论基础03->互斥锁(上),解决原子性问题

1.原子性

一个或多个操作在cpu执行的过程中不被中断的特性,称为原子性

2.如何解决原子性问题

源头是执行一半线程切换,禁止线程切换是不是就可以了?
操作系统的线程切换,是操作系统自己控制cpu进行的,所以禁止操作系统的cpu发生中断就可以禁止线程切换

单核cpu场景,同一时刻只有一个线程执行,禁止cpu中断,操作系统就不会重新调度线程,禁止了线程切换,获取cpu使用权的线程就可以不间断执行。两次写操作,要么都被执行,要么都没有被执行,具有原子性

多核cpu场景,同一时刻,可能2个线程同时运行,一个线程在cpu1核上,一个线程在cpu2核上,禁止cpu中断,只能保证cpu上的线程连续执行不被线程切换走,不能保证同一时刻只有一个线程执行。

同一时刻,只有一个线程执行,这个条件非常重要,我们成为互斥。我们对共享变量的修改,是互斥的,无论是单核还是多核cpu,都能保证原子性

3.简易锁模型

互斥,锁。
image
需要互斥执行的代码成为临界区。线程进入临界区之前,尝试加锁,如果成功,进入临界区。线程持有锁,否则就等待,直到持有锁的线程解锁。持有锁的线程执行完临界区代码后,解锁unlock

类比进坑锁门,出坑开门,如厕就是临界区。

4.改建的锁模型

锁和资源的关系
image

创建和资源关联的锁

5.java语言提供的同步锁synchronized

synchronized就是锁的一种实现,synchronized关键字可以修饰方法,修饰代码库,

代码范例


class X {
  // 修饰非静态方法
  synchronized void foo() {
    // 临界区
  }
  // 修饰静态方法
  synchronized static void bar() {
    // 临界区
  }
  // 修饰代码块
  Object obj = new Object();
  void baz() {
    synchronized(obj) {
      // 临界区
    }
  }
}  

synchronized 默认加上了加锁,解锁操作。
synchronized修饰代码库的时候,锁定了obj对象
synchronized修饰方法时候,锁定的是当前实例对象this
synchronized修饰静态方法时候,锁定的是当前类的class对象
修饰静态方法的具体含义


class X {
  // 修饰静态方法
  synchronized(X.class) static void bar() {
    // 临界区
  }
}

修饰非静态方法的具体含义


class X {
  // 修饰非静态方法
  synchronized(this) void foo() {
    // 临界区
  }
}

6.多线程共享数据修改问题

synchronized


class SafeCalc {
  long value = 0L;
  long get() {
    return value;
  }
  synchronized void addOne() {
    value += 1;
  }
  long get() { return value; }
}

synchronized修饰addOne方法后,一定能保证原子操作,
可见性呢?
6个happens-before中的管程中锁规则说明了,对锁的解锁happens-before与后续对这个锁的加锁操作

管程就是synchronized,synchronized修饰的临界区是互斥的,同一时刻只有一个线程执行临界区的代码

而解锁操作happens-before后续对这个锁的加锁操作。
多线程同时之同时addOne方法,可见性是保证的,A线程修改完后,解锁happens-before与B线程的加锁,数据的可见性有保证

get方法可见不?
答案是不可见,管程中只保证后续对这个锁的加锁的可见性,get方法没有加锁,所以没法保证可见性,优化成synchronized修饰就可以解决这个问题了
get和addOne用的是一把锁this
image

7.锁和受保护资源的关系

受保护资源和缩之间的关系是N:1,一把锁保护多个资源可以,多把锁保护一个资源不可行


class SafeCalc {
  static long value = 0L;
  synchronized long get() {
    return value;
  }
  synchronized static void addOne() {
    value += 1;
  }
}

修改数据用的是SafeCalc.class锁,而get()用的是this锁,
修改数据的synchronized可见性只保证向同锁的可见性
这两个临界区没有互斥关系,所以有并发问题了

8.总结

互斥锁,需要深知锁的对象和缩之间的关系,才能用好互斥锁。

原创:做时间的朋友
原文地址:https://www.cnblogs.com/PythonOrg/p/14931096.html