在java中,很多时候我们忽略的基本的知识,这是很致命的,只有搞懂Thread的基础知识,才能进一步探索:reentrantLock,AQS等。
1:Thread的线程状态到底有几种?
6种:
public enum State {/*** Thread state for a thread which has not yet started.尚未启动的线程的线程状态。*/NEW,/*** Thread state for a runnable thread. 可运行线程的线程状态。*/RUNNABLE,/*** A thread in the blocked state is waiting for a monitor lock* to enter a synchronized block/method or* reenter a synchronized block/method after calling* 等待获取锁的状态,或者等待获取重入锁的状态*/BLOCKED,/*** Thread state for a waiting thread. 线程等待状态调用以下方法都可以使一个线程进入等待状态 -》等待后必须通过notify(),notifyAll()才能唤醒,唤醒后才能重新去尝试获取CPU的执行权* <ul>* <li>{@link Object#wait() Object.wait} with no timeout</li>* <li>{@link #join() Thread.join} with no timeout</li>* <li>{@link LockSupport#park() LockSupport.park}</li>* </ul>*/WAITING,/*** Thread state for a waiting thread with a specified waiting time.* 有期限的等待,超时等待。以下方法:* sleep Thread.sleep* Object#wait(long) * join(long) Thread.join* LockSupport#parkNanos LockSupport.parkNanos* LockSupport#parkUntil LockSupport.parkUntil*/TIMED_WAITING,/*** Thread state for a terminated thread.* 线程结束*/TERMINATED;}
以上源码就解释了什么是阻塞,等待,但是没有挂起,为什么没有挂起状态,因为线程挂起这种操作已经过时,不建议使用了,这里不做过多讨论,有人愿意研究的,请自行搜索:线程挂起。不要搜跟阻塞,等待的区别,很多都说错了。
2:到底如何区分阻塞,等待?
一切回归原始:假设一个场景:多个线程任务执行在同一个单核cpu上,在我们获取临界资源的时候,为了线程安全,是要加锁的,假设锁是synchronized,那么同一时刻,只有一个线程任务能获取锁资源,那么其它的线程就进入了:blocked状态,等待获取锁的状态,如上代码BLOCKED, 此时,这些没有获取到锁的线程有一些条件:1:他们都是可以随时被CPU上下文切换获取到执行权的。2:他们虽然可以获取CPU执行的时间片,但是他们无法获取锁。所以被阻塞在这里了。
基于以上两点,那么他们处于阻塞状态。而第一个获取到锁的线程,是runnable状态。
如果有大量线程为了竞争同一把锁(同一临界资源)而发生大面积阻塞,就会形成线程饥饿。
那什么是等待?还是上面的场景,加入有一个线程(第一个线程)获取到了锁,那么其它任何线程尝试获取锁都会失败,一旦失败,调用方法让他们进入等待的话,他们就不能再获取到CPU执行权了,跟上面所说的阻塞特征的第一点冲突,另外,他们也不会一直尝试去获取锁了,因为他们拿不到CPU执行权,相当于一个瘫痪的人没有CPU的帮扶,是无法站起来的。 那么他们如何才能站起来,继续有机会获取CPU执行权,进而获取锁呢? --唤醒-》notify。 所以,假如一个线程是wait状态已经,如果没有任何线程去唤醒它,那它永远是死的,如果大量的线程一旦永无机会被唤醒的话,将会占用大量内存。
到这里我们一起看一下我sleep和wait的区别
sleep和wait_肥春勿扰的博客-CSDN博客