阻塞与挂起
挂起(suspend)
是指在操作系统进程管理将前台的进程暂停(suspend)并转入后台的动作。将进程挂起可以让用户在前台执行其他的进程。挂起的进程通常释放除CPU以外已经占有的系统资源,如内存等。
阻塞(block)
正在执行的进程由于发生某时间(如I/O请求、申请缓冲区失败等)暂时无法继续执行。此时引起进程调度,OS把处理机分配给另一个就绪进程,而让受阻进程处于暂停状态,一般将这种状态称为阻塞状态。
两者的区别
挂起是一种主动行为,因此恢复也应该要主动完成;
阻塞则是一种被动行为,恢复交由系统完成。而且挂起队列在操作系统里可以看成一个,而阻塞队列则是不同的事件或资源(如信号量)就有自己的队列。
- 运行中挂起,进入
静止就绪
状态,释放资源,自身对换到外存中,不释放锁,也不释放CPU,比如sleep(); - 而运行中阻塞,进入
活动阻塞
状态,释放资源,释放锁,释放CPU,比如wait()。
也就是说挂起在运行状态中要比阻塞靠前,挂起是一直在内存中定期运行自己,检查是否可以跳出当前状态;阻塞是自己不在运行,需要第三方检查自己是否可以跳出当前状态,在一定条件下也会由操作系统将其移出内存保存到外存中。
Java中的线程与锁与其的关系
Java 的 Thread.State 中定义了线程的 6 种状态,分别如下:
- NEW 未启动的,不会出现在 dump 文件中 (可以通过 jstack命令查看线程堆栈)
- RUNNABLE 正在 JVM 中执行的
- BLOCKED 被阻塞,等待获取监视器锁进入 synchronized 代码块或者在调用Object.wait 之后重新进入 synchronized 代码块
- WAITING 无限期等待另一个线程执行特定动作后唤醒它,也就是调用Object.wait 后会等待拥有同一个监视器锁的线程调用 notify/notifyAll来进行唤醒
- TIMED_WAITING 有时限的等待另一个线程执行特定动作
- 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 用的是乐观锁机制。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。