• 【并发编程】2.锁与等待通知机制


    互斥锁

    【并发编程理论】1.并发问题的由来
    中说明了原子性为 CPU在执行一个或多个操作的过程中不被中断。
    然而在多核CPU的情况下,有一些情况即使操作不被中断,也会引发线程安全问题。

    互斥性则为同一时刻只有一个线程执行,无论多核或者单核的情况都可以满足原子性。

    一、锁的设计思想

    • 1.把需要互斥执行的代码成为临界区
    • 2.线程在执行临界区代码时需要获取锁(同时只有一个线程可以获取锁,已被占用就要等待),并且加锁称为 Lock
    • 3.执行完临界区代码后释放锁,称为UnLock

    锁与临界区代码最好存在关联,这样可以使用最小粒度的锁来保护需要互斥

    二、Synchronized关键字

    是Java中锁的一种实现,可以用于修饰代码块,也可以用于修饰方法。

    	//加锁的是调用该方法的对象 this
    	public synchronized void  method(){
        }
    	//加锁的是 该类的 .class
        public static synchronized  void method2(){
        }
    	//加锁的是指定的对象 obj
        public   void method3(){
            synchronized(obj){
            }
        }
    

    synchronized 存在的问题申请资源的时候,如果申请不到,线程直接进入阻塞状态,而线程进入阻塞状态,啥都干不了,也释放不了线程已经占有的资源


    三、Lock

    Lock是JavaSDK提供的另一种实现互斥锁的方式,并且解决了synchronized关键字的资源不可抢占问题

    解决思路

    • 1.超时机制 指定时间内获取不到锁,不进入阻塞状态
    • 2.能够响应中断 给阻塞的线程发送中断信号的时候,能够唤醒它
    • 3.非阻塞的获取锁 当尝试获取锁失败,并不进入阻塞状态,而是直接返回

    对应Lock中的方法

    // 支持中断的 API
    void lockInterruptibly()
            throws InterruptedException;
    // 支持超时的 API
    boolean tryLock(long time, TimeUnit unit)
            throws InterruptedException;
    // 支持非阻塞获取锁的 API
    boolean tryLock();
    

    锁的使用范式

    class X {
            private final Lock rtl = new ReentrantLock();
            int value;
            public void addOne() {
                // 获取锁
                rtl.lock();
                try {
                    value+=1;
                } finally {
                    // 保证锁能释放
                    rtl.unlock();
                }
            }
        }
    

    四、死锁问题

    死锁问题本质是: 一组互相竞争资源 的线程因互相等待,导致“永久”阻塞的现象

    问题场景
    假设线程 T1 执行账户 A 转账户 B 的操作,账户 A.transfer(账户 B);
    线程 T2 执行账户 B 转账户 A 的操作,账户 B.transfer(账户 A)。

    此时程序就进入死锁,线程之间互相等待。

    要避免死锁就需要分析死锁发生的条件,只有以下这四个条件都发生时才会出现死锁:

    1. 互斥,共享资源 X 和 Y 只能被一个线程占用;

    2. 占有且等待,线程 T1 已经取得共享资源 X,在等待共享资源 Y 的时候,不释放共享资源 X;
      解决办法: 同时申请所有资源

    3. 不可抢占,其他线程不能强行抢占线程 T1 占有的资源;
      解决办法: 使用Lock API

    4. 循环等待,线程 T1 等待线程 T2 占有的资源,线程 T2 等待线程 T1 占有的资源,就是循环等待。
      解决办法: 对需要的资源进行排序 例如:全都按照从小到大的顺序


    五、线程间的通信:等待与通知机制

    当线程抢占锁的资源时,如果抢占不到就一直循环抢占,会非常的浪费资源。

    使用等待通知机制可以进行优化,在获取不到锁时等待,占用锁线程释放锁时通知所有等待的线程抢占锁。
    当不满足条件进入等待队列
    当锁被释放,等待队列中的线程去抢占锁

    java中提供的API是
    wait()、notify()、notifyAll()

    这些方法操作的都是互斥锁的对应的等待队列
    调用这些方法的对象应该是加锁的对象

  • 相关阅读:
    okhttp post用json传递参数
    android10获取相册图片
    从一页跳转到另一页返回后刷新
    获取应用图标并转为bitmap适配android10
    上拉更新,下拉刷新
    android 多图片上传
    04号团队-团队任务5:项目总结会
    codeforces987D bfs跑最短路
    layaair 物理
    TypeScript
  • 原文地址:https://www.cnblogs.com/shinyrou/p/13279294.html
Copyright © 2020-2023  润新知