0%

线程(进程)的阻塞与挂起

阻塞与挂起

挂起(suspend)

是指在操作系统进程管理将前台的进程暂停(suspend)并转入后台的动作。将进程挂起可以让用户在前台执行其他的进程。挂起的进程通常释放除CPU以外已经占有的系统资源,如内存等。

阻塞(block)

正在执行的进程由于发生某时间(如I/O请求、申请缓冲区失败等)暂时无法继续执行。此时引起进程调度,OS把处理机分配给另一个就绪进程,而让受阻进程处于暂停状态,一般将这种状态称为阻塞状态。

两者的区别

挂起是一种主动行为,因此恢复也应该要主动完成;

阻塞则是一种被动行为,恢复交由系统完成。而且挂起队列在操作系统里可以看成一个,而阻塞队列则是不同的事件或资源(如信号量)就有自己的队列。

  • 运行中挂起,进入静止就绪状态,释放资源,自身对换到外存中,不释放锁,也不释放CPU,比如sleep();
  • 而运行中阻塞,进入活动阻塞状态,释放资源,释放锁,释放CPU,比如wait()。

也就是说挂起在运行状态中要比阻塞靠前,挂起是一直在内存中定期运行自己,检查是否可以跳出当前状态;阻塞是自己不在运行,需要第三方检查自己是否可以跳出当前状态,在一定条件下也会由操作系统将其移出内存保存到外存中。

Java中的线程与锁与其的关系

Java 的 Thread.State 中定义了线程的 6 种状态,分别如下:

  1. NEW 未启动的,不会出现在 dump 文件中 (可以通过 jstack命令查看线程堆栈)
  2. RUNNABLE 正在 JVM 中执行的
  3. BLOCKED 被阻塞,等待获取监视器锁进入 synchronized 代码块或者在调用Object.wait 之后重新进入 synchronized 代码块
  4. WAITING 无限期等待另一个线程执行特定动作后唤醒它,也就是调用Object.wait 后会等待拥有同一个监视器锁的线程调用 notify/notifyAll来进行唤醒
  5. TIMED_WAITING 有时限的等待另一个线程执行特定动作
  6. TERMINATED 已经完成了执行

Thread.sleep

也就是说这是挂起(suspend),作用域在线程,持有CPU。

Object.wait/synchronized

也就是说这是阻塞(block),作用域在锁对象,释放CPU。

wait/notify是通过 JVM里的park/unpark 机制来实现的,在Linux下这种机制又是通过pthread_cond_wait/pthread_cond_signal实现。

Lock

也就是说这是挂起(suspend),作用域在锁对象,持有CPU。

Lock 用的是乐观锁机制。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。

参考