• 【Java并发入门】03 互斥锁(上):解决原子性问题


    原子性问题的源头是线程切换

    Q:如果禁用 CPU 线程切换是不是就解决这个问题了?
    A:单核 CPU 可行,但到了多核 CPU 的时候,有可能是不同的核在处理同一个变量,即便不切换线程,也有问题。

    所以,解决原子性的关键是「同一时刻只有一个线程处理该变量,也被称为互斥」。

    如何做到呢?用「锁」。

    一、锁模型

    一)简易锁模型

    一般看到的锁模型长下面这样。

    但对于这个模型,会有几个疑问:

    • 锁的是什么?
    • 临界区的这一堆代码相关的都被锁了?
    • 保护的又是什么?

    image.png

    二)改进后的锁模型

    用下面这个模型来解释就解答了上面几个问题:

    • 要保护的是临界区中的资源 R
    • 因此要为 R 创建一个对应的锁 LR
    • 需要处理资源 R 的时候先加锁,处理完之后解锁

    image.png

    要注意的是:

    • 一个资源必须和锁对应,不能用 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) {
          // 临界区
        }
      }
    }  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    Q:synchronized 没看到 lock 和 unlock?
    A:在编译的时候会做转换,synchronized起始的地方加锁,结束的地方解锁。

    Q:那么 synchronized 锁的是什么呢?
    A:当修饰静态方法时,锁定的是当前类的 Class 对象,在上面的例子中就是 Class X;
    当修饰非静态方法时,锁定的是当前实例对象 this。
    当修饰代码块时,括号中写的是啥就锁啥。

    (可能不准确)
    Class 对象是用来保存类信息的,可以理解为元数据?
    实例对象则是每一个 new 出来的特殊的个体

    Synchronized 实例

    public class SynchronizedTT  {
        private int value = 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()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    💡思考:如果 printValue() 不添加 synchronized 关键字,会造成什么样的结果?
    A:有可能会先执行了 addValue 在执行 print 但得到的却是增加之前的数值。

    三、锁和受保护资源的关系

    要点:

    • 一把锁可以保护多个资源
    • 但是一个资源只能用一把锁保护
    • 受保护资源和锁之间的关联关系是 N:1 的关系

    💡思考:如果用多把锁锁同一个资源会出现什么情况?

    下面例子:
    synchronized 是不同的锁,就和没锁一样。

    public class SynchronizedTT  {
        private static int value = 0;
    
        public synchronized void printValue() {
            System.out.println(value);
        }
    
        public synchronized static void addValue() throws InterruptedException {
            Thread.sleep(1000);
            value += 1;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    image.png

  • 相关阅读:
    算法详解——贪心算法
    王树森Transformer学习笔记
    如何解决Smartsheet 登录时遇到的问题
    Haproxy 服务
    Java 并发高频面试题:聊聊你对 AQS 的理解?
    docker搭建hadoop集群 个人总结
    python - ExcelWriter.book 无法设置属性 ‘book‘
    (附源码)springboot太原学院贫困生申请管理系统 毕业设计 101517
    VOLO: Vision Outlooker for Visual Recognition 阅读笔记
    系统和系统实例-软件方法(下)第9章分析类图案例篇Part07
  • 原文地址:https://blog.csdn.net/shuofxz/article/details/128155746