线程运行状态图解
java: linux: 阻塞状态被java细分为了 blocker waiting time_waiting
需要在synchronized中使用——wait()和notify()
java中当调用一个锁对象的wait或notify方法时,如当前锁的状态是偏向锁或轻量级锁则会先膨胀成重量级锁。
- JDK在Object对象中提供了2个非常重要的接口线程方法wait方法和notify方法,也就是所有Java对象都有这2个方法。
- wait()可以设置等待时间也可以不设,这两种情况调用wait()后,线程的状态是不一样的。设置等待时间的情况,调用wait()后,线程状态是TIMED_WAITING,时间到后自动加入EntryList进行BLOCKED。不设等待时间,线程状态是WAITING,此时代码停在wait()的位子,重新获得执行的条件后会从当前位子继续执行。
- 我们有一个person对象,如果一个线程T1调用person.wait(),那么这个线程T1就会进入person对象的等待队列,线程状态是WAITING,在这个等待队列中可能还有线程T2,线程T3,线程T4。
- 现在T5线程获取到锁进入同步代码块,此时调用person.notify()方法,它就会从这个等待队列中随机选一个线程,并将其唤醒后加入EntryList进行BLOCKED,在这里这个选择是不公平的,也就是选择线程T1,T2,T3,T4是随机的,当然了也可以调用person,notifyAll()方法,这个方法会把线程T1,T2,T3,T4全部唤醒后加入EntryList进行BLOCKED。
- 需要注意的是person.wait()方法并不是随便调用的,它必须包含在对应的synchronzied中,无论是wait()方法还是notify()方法都需要首先获取目标对象上的一个监视器。
wait()与sleep()【不需要前提条件就可以使用】的区别
- wait()来自Object类,sleep()来自Thread类。
- 成功调用锁对象的wait方法后,当前线程会释放锁,进入Monitor的WaitSet中。而调用Thread.sleep()是不会释放锁的。
- sleep(millionseconds)需要指定一个睡眠时间,时间一到会自然唤醒。wait()传入不为0的正整数会在时间到后自动进入阻塞队列等待,wait()则需要配合notify()或者notifyAll()使用。
- sleep不需要强制和synchronized配合使用,但wait需要。
需要Lock才能使用——await()与signal()
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
线程等待让出资源进入条件变量阻塞:持有lock的线程调用await() 线程唤醒:持有lock的线程signal() 唤醒所有线程:持有lock的线程signalAll()
底层调用的是:park unpark
不需要前提条件就可以使用——park()与unpark()
- park/unpark不需要在同步块或者方法内执行。
- park/unpark不需要先执行park,在执行unpark,无需在意顺序。
- 当前线程调用LockSupport.park()方法会让当前线程从 RUNNABLE-----> WAITING
- 调用LockSupport.unpark(目标线程)或调用了线程的interrupt(),会让目标线程从 WAITING–>RUNNABLE
调用park方法
调用unPark方法
interrupt()
当对一个线程,调用 interrupt() 时:
- ① 如果线程处于被阻塞状态(例如处于sleep, wait, join 等状态),那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常。仅此而已。
- ② 如果线程处于正常活动状态,那么会将该线程的中断标志设置为 true,仅此而已。被设置中断标志的线程将继续正常运行,不受影响。interrupt() 并不能真正的中断线程,需要被调用的线程自己进行配合才行。
- 也就是说,一个线程如果有被中断的需求,那么就可以这样做。
① 在正常运行任务时,经常检查本线程的中断标志位,如果被设置了中断标志就自行停止线程。 ② 在调用阻塞方法时正确处理InterruptedException异常。(例如,catch异常后就结束线程。)
Thread thread = new Thread(() -> {
while (!Thread.interrupted()) {
}
});
thread.start();
thread.interrupt();
|