• 第一部分:并发理论基础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.总结

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

    原创:做时间的朋友
  • 相关阅读:
    C++ 11 右值引用以及std::move
    poj2299--B
    Linux Socket编程注意事项
    Using Qt to build an Omi App for iOS (and Android)
    openwrt 3g模块上网
    详谈隐藏Tabbar的几种方法
    ZOJ 3529 A Game Between Alice and Bob(博弈论-sg函数)
    uva 10574
    【MySQL案例】HA: GTID_MODE配置不一致
    Swift UIView 层次调整
  • 原文地址:https://www.cnblogs.com/PythonOrg/p/14931096.html
Copyright © 2020-2023  润新知