一.线程包括哪些状态?
1.新建NEW:创建线程对象进入NEW状态。
2.可执行RUNNABLE:可执行又分为两种:
(1)就绪runnable:线程调用start()方法进入runnable。
(2)执行running:处于runnable的线程如果竞争到cpu使用权,则进入running执行;若失去cpu使用权,则重新回到runnable。
3.终止TERMINATED:线程执行完或是主线程的main()函数执行完,则线程终止,线程一旦终止则不能复生。
4.阻塞BLOCKED:处于RUNNABLE.running的线程如果没有竞争到锁(synchronized或lock),则进入BLOCKED阻塞。竞争到锁后再切换到RUNNABLE.runnable等待执行。
5.等待WAITING:处于RUNNABLE.running的线程如果调用wait()则进入WAITING等待,直到被其他线程调用notify()唤醒,再切换到RUNNABLE.runnable等待执行。
6.计时等待TIMED_WAITING:处于RUNNABLE.running的线程如果调用sleep()或wait(long)则进入计时等待状态,直到计时结束主动醒来,再切换到RUNNABLE.runnable等待执行。
注:线程的状态发生顺序是不可逆的:RUNNABLE.runnable-->RUNNABLE.running-->(BLOCKED、WAITING、TIMED_WAITING)-->RUNNABLE.runnable。
二.如何保证多个线程按先后顺序执行?
1.使用join()方法:thread1.join()表示执行到此代码的线程,会进入TIMED_WAITING等待,只有等到thread1线程执行完,当前线程才能醒来继续执行。
2.使用wait()+notify():后执行的线程调用wait()进入等待,先执行的线程执行完才调用notify()唤醒下一个线程。
三.notify()和notifyAll()的区别
1.notify()会随机唤醒一个因调用wait()或wait(long)进入等待的线程。
2.notifyAll()会唤醒所有因调用wait()或wait(long)进入等待的线程。
四.wait()和notify()的关系
1.获取锁:线程要使用wait()或notify()都必须先获取到锁。
(1)线程调用wait()进入WAITING,调用wait(long)进入TIMED_WAITING,都能被notify()唤醒。
(2)线程调用notify()会随机唤醒一个因调用wait()或wait(long)进入等待的线程。
(3)wait()和notify()通过对象锁关联:若thread1获得锁lock1,调用wait()进入等待;则其他线程只有同样获取锁lock1,才能调用notify()唤醒thread1,使用的是同一把锁。
2.释放锁:
(1)线程调用wait()或调用wait(long)进入等待后,会释放锁给其他线程使用,synchronized剩余代码不再执行。
(2)线程调用notify()唤醒其他线程后,不会释放锁,会继续执行完synchronized剩余代码。
五.wait()和sleep()的关系
1.方法归属不同
(1)wait()和wait(long)都是Object类的成员方法,每个对象都有。
(2)sleep()是Thread类独有的方法。
2.作用相同
(1)调用wait()进入WAITING,调用wait(long)或sleep()进入TIMED_WAITING。
(2)无论进入哪种状态,作用都是让cpu暂时放弃cpu使用权。
3.唤醒方式不同
(1)调用wait()进入等待的线程,只能被其他线程调用notify()或notifyAll()唤醒,若没有被唤醒则永久等待。
(2)调用wait(long)进入等待的线程,可以被其他线程调用notify()或notifyAll()唤醒,也可以等到计时结束主动醒来。
(3)调用sleep()进入等待的线程,只能等到计时结束主动醒来,或者通过interrupt()强行中断线程。
4.使用的条件不同
(1)线程要调用wait()或wait(long)都必须要先获取锁。
(2)线程要调用sleep()无需获取锁,直接使用即可。
5.对锁的处理不同
(1)线程调用wait()或wait(long)进入等待后,都会释放锁给其他线程使用。
(2)线程调用sleep()进入等待后,不会释放锁(抱着锁睡觉)。等计时结束主动醒来后,也会继续持有锁去切换到RUNNABLE.runnable等待执行。
六.如何停止一个正在运行的线程?
1.自定义变量充当退出标志,线程通过判断变量值来决定是否退出执行。为了防止编译器优化,该变量必须用volatile关键字修饰。
2.使用interrupt()中断线程。线程调用interrupt()可能会修改线程内部的中断标记,线程通过判断中断标记来决定是否退出执行。
(1)打断阻塞或等待(wait()、sleep()、join())的线程:阻塞或等待的线程调用interrupt()会直接抛出InterruptedException异常,中断执行。但该线程的中断标记不会被修改,因为还未修改就异常退出了。
(2)打断正常运行的线程:正在运行的线程调用interrupt()会修改线程内部的中断标记,但仅仅是修改了标记而已,线程会继续正常执行。若要达到中断效果还必须自己编写逻辑代码,通过判断线程的中断标记来让线程退出执行。