【Java并发笔记】03 互斥锁(上)(解决原子性问题)

原子性问题的源头是线程切换
Q:如果禁用 CPU 线程切换是不是就解决这个问题了?
A:单核 CPU 可行,但到了多核 CPU 的时候,有可能是不同的核在处理同一个变量,即便不切换线程,也有问题。
【【Java并发笔记】03 互斥锁(上)(解决原子性问题)】所以,解决原子性的关键是「同一时刻只有一个线程处理该变量,也被称为互斥」。
如何做到呢?用「锁」。
一、锁模型 一)简易锁模型
一般看到的锁模型长下面这样。
但对于这个模型,会有几个疑问:

  • 锁的是什么?
  • 临界区的这一堆代码相关的都被锁了?
  • 保护的又是什么?
【Java并发笔记】03 互斥锁(上)(解决原子性问题)
文章图片

二)改进后的锁模型
用下面这个模型来解释就解答了上面几个问题:
  • 要保护的是临界区中的资源 R
  • 因此要为 R 创建一个对应的锁 LR
  • 需要处理资源 R 的时候先加锁,处理完之后解锁
【Java并发笔记】03 互斥锁(上)(解决原子性问题)
文章图片

  • 一个资源必须和锁对应,不能用 A 锁去锁 B 资源
二、Java 提供的锁技术 Java 提供了多种技术,这里仅谈及 Synchronized
Synchronized 关键字
Java 语言提供的 synchronized 关键字,就是锁的一种实现。synchronized 关键字可以用来修饰方法,也可以用来修饰代码块。
class X { // 修饰非静态方法 synchronized void foo() { // 临界区 } // 修饰静态方法 synchronized static void bar() { // 临界区 } // 修饰代码块 Object obj = new Object(); void baz() { synchronized(obj) { // 临界区 } } }

Q:synchronized 没看到 lock 和 unlock?
A:在编译的时候会做转换,synchronized起始的地方加锁,结束的地方解锁。
Q:那么 synchronized 锁的是什么呢?
A:当修饰静态方法时,锁定的是当前类的 Class 对象,在上面的例子中就是 Class X;
当修饰非静态方法时,锁定的是当前实例对象 this。
当修饰代码块时,括号中写的是啥就锁啥。
(可能不准确)
Class 对象是用来保存类信息的,可以理解为元数据?
实例对象则是每一个 new 出来的特殊的个体
Synchronized 实例
public class SynchronizedTT{ private int value = https://www.it610.com/article/0; //public void printValue() { public synchronized void printValue() { System.out.println(this.value); }public synchronized void addValue() throws InterruptedException { Thread.sleep(1000); this.value += 1; } }// 开两个线程,一个先调用 addValue(),另一个后调用 printValue()

思考:如果 printValue() 不添加 synchronized 关键字,会造成什么样的结果?
三、锁和受保护资源的关系 要点:
  • 一把锁可以保护多个资源
  • 但是一个资源只能用一把锁保护
  • 受保护资源和锁之间的关联关系是 N:1 的关系
思考:如果用多把锁锁同一个资源会出现什么情况?
下面例子:
synchronized 是不同的锁,就和没锁一样。
public class SynchronizedTT{ private static int value = https://www.it610.com/article/0; public synchronized void printValue() { System.out.println(value); }public synchronized static void addValue() throws InterruptedException { Thread.sleep(1000); value += 1; } }

【Java并发笔记】03 互斥锁(上)(解决原子性问题)
文章图片

    推荐阅读