Java-操作系统-7

1.28 什么情况下会产生死锁?

参考回答

如果在计算机系统中同时具备下面四个必要条件时,那么会发生死锁。换句话说,只要下面四个条件有一个不具备,系统就不会出现死锁。

  1. 互斥条件。即某个资源在一段时间内只能由一个进程占有,不能同时被两个或两个以上的进程占有。这种独占资源如CD-ROM驱动器,打印机等等,必须在占有该资源的进程主动释放它之后,其它进程才能占有该资源。这是由资源本身的属性所决定的。如独木桥就是一种独占资源,两方的人不能同时过桥。

    #include <list>
    #include <mutex>
    #include <algorithm>
    
    std::list<int> some_list;    // 1
    std::mutex some_mutex;    // 2
    
    void add_to_list(int new_value)
    {
      std::lock_guard<std::mutex> guard(some_mutex);    // 3
      some_list.push_back(new_value);
    }
    
    bool list_contains(int value_to_find)
    {
      std::lock_guard<std::mutex> guard(some_mutex);    // 4
      return std::find(some_list.begin(),some_list.end(),value_to_find) != some_list.end();
    }

    代码中有一个全局变量①,这个全局变量被一个全局的互斥量保护②。add_to_list()③和list_contains()④函数中使用std::lock_guard<std::mutex>,使得这两个函数中对数据的访问是互斥的:list_contains()不可能看到正在被add_to_list()修改的列表。

  1. 不剥夺条件。进程所获得的资源在未使用完毕之前,资源申请者不能强行地从资源占有者手中夺取资源,而只能由该资源的占有者进程自行释放。如过独木桥的人不能强迫对方后退,也不能非法地将对方推下桥,必须是桥上的人自己过桥后空出桥面(即主动释放占有资源),对方的人才能过桥。

     
  2. 请求和保持条件。进程至少已经占有一个资源,但又申请新的资源;由于该资源已被另外进程占有,此时该进程阻塞;但是,它在等待新资源之时,仍继续占用已占有的资源。还以过独木桥为例,甲乙两人在桥上相遇。甲走过一段桥面(即占有了一些资源),还需要走其余的桥面(申请新的资源),但那部分桥面被乙占有(乙走过一段桥面)。甲过不去,前进不能,又不后退;乙也处于同样的状况。

     
  3. 循环等待条件。存在一个进程等待序列{P1,P2,...,Pn},其中P1等待P2所占有的某一资源,P2等待P3所占有的某一源,......,而Pn等待P1所占有的的某一资源,形成一个进程循环等待环。就像前面的过独木桥问题,甲等待乙占有的桥面,而乙又等待甲占有的桥面,从而彼此循环等待。

    std::mutex m;
    void f()
    {
    // ....
    std::lock_guard lock(m); // 1 子线程锁住互斥量m
    // ...
    }
    int main()
    {
    std::thread t(f);
    std::lock_guard lock(m); // 2 主线程锁住互斥量m
    // ...
    t.join(); // 3 等待子线程结束
    return 0;
    }

    上述过程可能导致在2处上锁,然后子线程在1处发生阻塞,最后主线程在3处一直等待子线程结束,无穷等待下去。

上面提到的这四个条件在死锁时会同时发生。也就是说,只要有一个必要条件不满足,则死锁就可以排除。

1.29 说一说你对自旋锁的理解。

参考回答

旋锁的定义:当一个线程尝试去获取某一把锁的时候,如果这个锁此时已经被别人获取(占用),那么此线程就无法获取到这把锁,该线程将会等待,间隔一段时间后会再次尝试获取。这种采用循环加锁 -> 等待的机制被称为自旋锁(spinlock)

自旋锁有以下特点

  • 用于临界区互斥
  • 在任何时刻最多只能有一个执行单元获得锁
  • 要求持有锁的处理器所占用的时间尽可能短
  • 等待锁的线程进入忙循环

自旋锁存在的问题

  • 如果某个线程持有锁的时间过长,就会导致其它等待获取锁的线程进入循环等待,消耗CPU。使用不当会造成CPU使用率极高。
  • 无法满足等待时间最长的线程优先获取锁。不公平的锁就会存在“线程饥饿”问题。

自旋锁的优点

  • 自旋锁不会使线程状态发生切换,一直处于用户态,即线程一直都是active的;不会使线程进入阻塞状态,减少了不必要的上下文切换,执行速度快
  • 非自旋锁在获取不到锁的时候会进入阻塞状态,从而进入内核态,当获取到锁的时候需要从内核态恢复,需要线程上下文切换。(线程被阻塞后便进入内核(Linux)调度状态,这个会导致系统在用户态与内核态之间来回切换,严重影响锁的性能)

自旋锁与互斥锁的区别

  • 自旋锁与互斥锁都是为了实现保护资源共享的机制。
  • 无论是自旋锁还是互斥锁,在任意时刻,都最多只能有一个保持者。
  • 获取互斥锁的线程,如果锁已经被占用,则该线程将进入睡眠状态;获取自旋锁的线程则不会睡眠,而是一直循环等待锁释放。

1.30 说一说你对悲观锁的理解。

参考回答

悲观锁总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。

1.31 说一说你对乐观锁的理解。

参考回答

乐观锁总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。

1.32 CAS在什么地方用到过吗?

参考回答

  • CAS是英文单词CompareAndSwap的缩写,中文意思是:比较并替换。CAS需要有3个操作数:内存地址V,旧的预期值A,即将要更新的目标值B。CAS指令执行时,当且仅当内存地址V的值与预期值A相等时,将内存地址V的值修改为B,否则就什么都不做。整个比较并替换的操作是一个原子操作。

  • 高并发环境下,对同一个数据的并发读(两边都读出余额是100)与并发写(一个写回28,一个写回38)导致的数据一致性问题。

    解决方案是在set写回的时候,加上初始状态的条件compare,只有初始状态不变时,才允许set写回成功,这是一种常见的降低读写锁冲突,保证数据一致性的方法。

1.33 谈谈IO多路复用。

参考回答

  1. IO多路复用是一种同步IO模型,实现一个线程可以监视多个文件句柄;一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作;没有文件句柄就绪时会阻塞应用程序,交出cpu。多路是指网络连接,复用指的是同一个线程。

  2. IO多路复用有三种实现方式:select, poll, epoll

    (1)select:时间复杂度O(n),它仅仅知

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

Java岗位面试真题宝典 文章被收录于专栏

本面试宝典均来自校招面试题目大数据进行的整理

全部评论
IO多路复用:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力
点赞 回复 分享
发布于 2022-09-03 18:08 福建

相关推荐

哈哈哈,你是老六:百度去年裁员分评不好,赶紧弄点红包
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务