600 条最强 Linux 命令总结

C++函数对象包装器std::function

  返回  

操作系统笔记

2021/7/20 16:19:16 浏览:

1、死锁

死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象。

死锁产生的四个必要条件?

  • 1、互斥条件:进程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一进程所占用。
  • 2、请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放。
  • 3、不剥夺条件:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放。
  • 4、环路等待条件:在发生死锁时,必然存在一个进程—资源的环形链。

解决死锁的基本方法?

预防死锁:

  • 资源一次性分配:一次性分配所有资源,这样就不会再有请求了:(破坏请求条件
  • 只要有一个资源得不到分配,也不给这个进程分配其他的资源:(破坏请保持条件
  • 可剥夺资源:即当某进程获得了部分资源,但得不到其它资源,则释放已占有的资源(破坏不可剥夺条件
  • 资源有序分配法:系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反(破坏环路等待条件

避免死锁:

  • 银行家算法:首先需要定义状态和安全状态的概念。系统的状态是当前给进程分配的资源情况。

    当进程请求资源时,假设同意该请求,看系统的状态,安全,同意,不安全,阻塞
    
    Resource(系统每种资源的总量)
    
    Available (未分配的每种资源总量)
    
    Claim        (该请求对资源的请求)
    
    Allocation(当前分配给进程的资源)
    

检测死锁:

1、每个进程和资源都有唯一号码

2、建立资源分配表和进程等待表

解除死锁:

1、剥夺资源:从其他进程种,抽取资源

2、撤销进程:撤销死锁进程或代价最小的进程

死锁检测:

1、Jstack

2、Jconsole JDK自带检测

2、线程和进程、协程的区别

Q: 进程是系统资源分配的最小单位,线程是cpu资源分配的基本单位,协程是比线程更加轻量级。

一个进程可以包含多个线程,一个线程可以包含多个协程。进程都有自己的虚拟地址空间,而线程共享进程中的虚拟地址空间,所以线程的切换不涉及到虚拟地址的切换。

进程切换涉及到虚拟地址空间的切换。

线程切换需要进入内核态,然后进行上下文切换。

协程被操作系统内核所管理,而完全是由程序所控制,也就是用户态。协程的开销要远远比线程小。

线程创建方式

  1. 通过继承Thread创建线程

    1. 定义Thread类的子类,重写run()方法,run()方法里面是线程的方法体。
    2. 创建子类对象,即线程对象
    3. 线程对象调用start()方法启动线程。
  2. 通过实现Runnable接口创建线程

    1. 定义Runnable接口的实现类,重写run()方法,run()方法里面是线程的方法体。
    2. 创建Runnable实现类对象,依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
    3. 线程对象调用start()方法启动线程。

注意:run()只是线程想要执行的内容,没有返回值。

  1. 通过实现Callable和FutureTask创建线程
    1. 定义Callable接口的实现类,重写call()方法,call()方法里面是线程的方法体,有返回值。
    2. 创建Callable实现类对象,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
    3. 使用FutureTask对象作为Thread对象的target创建并启动新线程。
    4. 调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

注意:call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。

线程几种状态

在这里插入图片描述

  • 新建状态(New):
    用new语句创建的线程处于新建状态,此时它和其他Java对象一样,仅仅在堆区中被分配了内存。
  • 就绪状态(Runnable):
    当一个线程对象创建后,其他线程调用它的start()方法,该线程就进入就绪状态,Java虚拟机会为它创建方法调用栈和程序计数器。处于这个状态的线程位于可运行池中,等待获得CPU的使用权。
  • 运行状态(Running):
    处于这个状态的线程占用CPU,执行程序代码。只有处于就绪状态的线程才有机会转到运行状态。
  • 阻塞状态(Blocked):
    阻塞状态是指线程因为某些原因放弃CPU,暂时停止运行。当线程处于阻塞状态时,Java虚拟机不会给线程分配CPU。直到线程重新进入就绪状态,它才有机会转到运行状态。
    1. 等待阻塞:通过调用线程的wait()方法,让线程等待某工作的完成。

    2. 同步阻塞:线程在获取synchronized同步锁失败(因为锁被其他线程占用),它会进入同步阻塞状态。

    3. 其他阻塞:通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或超时、或者I/O处理完毕时,线程重新转入就绪状态。

  • 死亡状态(Dead):
    当线程退出run()方法时,就进入死亡状态,该线程结束生命周期。

线程方法

  • 1、Thread.sleep(long millis),一定是当前线程调用此方法,当前线程进入TIMED_WAITING状态,但不释放对象锁,millis后线程自动苏醒进入就绪状态。作用:给其它线程执行机会的最佳方式。

  • 2、Thread.yield(),一定是当前线程调用此方法,当前线程放弃获取的CPU时间片,但不释放锁资源,由运行状态变为就绪状态,让OS再次选择线程。作用:让相同优先级的线程轮流执行,但并不保证一定会轮流执行。实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。Thread.yield()不会导致阻塞。该方法与sleep()类似,只是不能由用户指定暂停多长时间。

  • 3、thread.join()/thread.join(long millis),当前线程里调用其它线程t的join方法,当前线程进入WAITING/TIMED_WAITING状态,当前线程不会释放已经持有的对象锁。线程t执行完毕或者millis时间到,当前线程一般情况下进入RUNNABLE状态,也有可能进入BLOCKED状态(因为join是基于wait实现的)。

  • 4、obj.wait(),当前线程调用对象的wait()方法,当前线程释放对象锁,进入等待队列。依靠notify()/notifyAll()唤醒或者wait(long timeout) timeout时间到自动唤醒。
    obj.notify()唤醒在此对象监视器上等待的单个线程,选择是任意性的。notifyAll()唤醒在此对象监视器上等待的所有线程。

  • 5、LockSupport.park()/LockSupport.parkNanos(long nanos),LockSupport.parkUntil(long deadlines), 当前线程进入WAITING/TIMED_WAITING状态。对比wait方法,不需要获得锁就可以让线程进入WAITING/TIMED_WAITING状态,需要通过LockSupport.unpark(Thread thread)唤醒。

3、用户态、内核态

内核态:cpu可以访问内存的所有数据,包括外围设备,例如硬盘,网卡,cpu也可以将自己从一个程序切换到另一个程序。

用户态:只能受限的访问内存,且不允许访问外围设备,占用cpu的能力被剥夺,cpu资源可以被其他程序获取。

所有用户程序都是运行在用户态的, 但是有时候程序确实需要做一些内核态的事情, 例如从硬盘读取数据, 或者从键盘获取输入等. 而唯一可以做这些事情的就是操作系统, 所以此时程序就需要先操作系统请求以程序的名义来执行这些操作.

用户态切换到内核态的方式

a. 系统调用
b. 异常
c. 外围设备的中断

4、悲观锁、乐观锁

5、线程池

工作原理:1、< corepoolsize 直接加入线程池工作

2、> corepoolsuze 加入队列 ,成功等待,失败 <= maximumpoolsize 进入线程池,> maximunpoolsize 拒绝策略

ThreadPoolExecutor的有哪些参数

corepoolsize, 线程数

MaximumPoolSize,最大线程数

BlockQueue,阻塞队列(

1、无界队列 (LinkedBlockingQueue)一直往后加

2、有界对列  FIFO (ArrayBlockingQueue), 优先对列(PriorityBlockingQueue)

3、同步移交对列 SynchronizedQueue

ArrayBlockingQueue中入队和出队操作,使用同一个lock,所以无法并发。
LinkedBlockingQueue读和写有两把锁ReentrantLock takeLock和putLock,它们之间的操作互相不受干扰,因此两种操作可以并行完成。

LinkedBlockingQueue的吞吐量要高于ArrayBlockingQueue。

handle,队列满后拒绝策略

AbortPolicy                      抛出RejectedExecutionException

DiscardPolicy                  什么也不做,直接忽略

DiscardOldestPolicy       丢弃执行队列中最老的任务,尝试为当前提交的任务腾出位置

CallerRunsPolicy             直接由提交任务者执行这个任务

keepAliveTime,空闲线程等待时间

unit,时间单位

threadFactory 创建线程的工厂类

线程池种类

(single,scheme,fixed,cache),初始化线程种类、拒绝策略、线程池执行。

线程池引发的故障到底怎么排查

    jps -l找到项目的进程号(jps -l)

    jstack dump线程信息,如下会将线程信息dump到一个名为thread.txt的文件中(jstack 16555 > thread.txt)

6、进程间通信方式有哪些,优缺点呢

(管道、消息队列、共享存储、信号、信号量、套接字)

7、并发与并行?

并发是指多个事件在同一时间段内发生,因此这多个事件是发生在同一实体上的。并发的本质是一个cpu在多个的程序之间进行多路复用,也就是多个用户共享同一个物理资源。

并行是指多个事件在同一时刻发生,因此这多个事件是发生多个实体上。并行的本质是不同的程序不同的cpu同时 运行。

并发编程的目标是充分的利用处理器的每一个核,以达到最高的处理性能。而并发编程可能会遇到很多问题,比如 :内存泄漏、上下文切换、线程安全、死锁等问题。

并发编程三要素:

  • 原子性:原子,即一个不可再被分割的颗粒。原子性指的是一个或多个操作要么全部执行成功要么全部执行失败。
  • 可见性:一个线程对共享变量的修改,另一个线程能够立刻看到。(synchronized,volatile)
  • 有序性:程序执行的顺序按照代码的先后顺序执行。(处理器可能会对指令进行重排序)

(原子性(单一线程)、可见性 (内存强制刷新)、有序性(as-id-serial、happens-before))

出现线程安全问题的原因:

  • 线程切换带来的原子性问题
  • 缓存导致的可见性问题
  • 编译优化带来的有序性问题

解决办法:

  • JDK Atomic开头的原子类、synchronized、LOCK,可以解决原子性问题
  • synchronized、volatile、LOCK,可以解决可见性问题
  • Happens-Before 规则可以解决有序性问题

8、多线程

多线程:多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务。

多线程的好处:

  • 可以提高 CPU 的利用率。在多线程程序中,一个线程必须等待的时候,CPU 可以运行其它的线程而不是等待,这样就大大提高了程序的效率。也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。

多线程的劣势:

  • 线程也是程序,所以线程需要占用内存,线程越多占用内存也越多;

  • 多线程需要协调和管理,所以需要 CPU 时间跟踪线程;

  • 线程之间对共享资源的访问会相互影响,必须解决竞用共享资源的问题。

9、线程同步和互斥?

同步就是协同步调,按预定的先后次序进行运行。这里的同步千万不要理解成那个同时进行,应是指协同、协助、互相配合

线程同步是指多线程 通过特定的设置(如互斥量,事件对象,临界区)来控制线程之间的执行顺序(即所谓的同步) 也可以说是在线程之间通过同步建立起执行顺序的关系,如果没有同步,那线程之间是各自运行各自的!

线程互斥是指对于共享的进程系统资源,在各单个线程访问时的排它性。当有若干个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源。线程互斥可以看成是一种特殊的线程同步(下文统称为同步)。

并发编程面试题(2020最新版)

联系我们

如果您对我们的服务有兴趣,请及时和我们联系!

服务热线:18288888888
座机:18288888888
传真:
邮箱:888888@qq.com
地址:郑州市文化路红专路93号