线程的五种状态
新建(New) 就绪(Ready) 运行(Running) 阻塞(Blocked) 死亡(Dead)
新建状态:New
在Java中使用new关键字创建一个线程,新创建的线程将处于新建状态。在创建线程时主要是为线程分配内存并初始化其成员变量的值
就绪状态:Runnable
JVM完成了方法调用栈和程序计数器的创建,等待该线程的调度和运行。
运行状态:Running
就绪状态的线程在竞争到CPU的使用权并开始执行run方法的线程执行体时,会转为运行状态,处于运行状态的线程的主要任务就是执行run方法中的逻辑代码。
阻塞状态:Blocked
运行中的线程会主动或被动地放弃CPU的使用权并暂停运行,此时该线程将转为阻塞状态,直到再次进入可运行状态,才有机会再次竞争到CPU 使用权并转为运行状态。分为以下三种
- 等待阻塞: 在运行状态的线程调用o.wait方法时,JVM会把该线程放入等待队列(Waitting Queue)中,线程转为阻塞状态。
- 同步阻塞:在运行状态的线程尝试获取正在被其他线程占用的对象同步锁时, JVM会把该线程放入锁池(LockPool)中,此时线程转为阻塞状态。
- 其他阻塞: 运行状态的线程在执行Thread.sleep(long ms), Thread.join()或者发出I/O请求时,JVM会把该线程转为阻塞状态。直到sleep()状态超时、Thread.join()等待线程终止或超时,或者I/O处理完毕,线程才重新转为可运行状态。
线程死亡:Dead
线程在以下面三种方式结束后转为死亡状态
- 线程正常结束:run方法或call方法执行完成。
- 线程异常退出:运行中的线程抛出一个Error 或未捕获的Exception,线程异常退出。
- 手动结束:调用线程对象的stop方法手动结束运行中的线程(该方式会瞬间释放线程占用的同步对象锁,导致锁混乱和死锁,不推荐使用)。
线程的基本方法
- 线程相关的基本方法有wait、notify、notifyAll、sleep、join、yield 等,这些方法控制线程的运行,并影响线程的状态变化。
线程等待:wait方法 |
|
线程睡眠:sleep方法 |
|
线程让步:yield方法 |
|
线程中断:interrupt方法 |
|
线程加入:join方法 |
|
线程唤醒:notify方法 |
|
后台守护线程:setDaemon方法 |
|
代码实例
public class ThreadDemo2 {
public static void main(String [] args){
Thread thread0 = new Thread(new ThreadTask());
Thread thread1 = new Thread(new ThreadTask());
for(int i= 0; i < 100; i++){
System.out.println(Thread.currentThread().getName() + "\t\t" + i);
if(i == 0){
thread0.start();
thread1.start();
//让主线程自动放弃CPU的使用权限,让thread0和thread1就绪完了以后就能开始执行
//也就是让主线程就绪,thread0和thread1都处于就绪状态,OS会自己决定调用哪个
//还有可能会轮换到main线程
Thread.yield();//使线程由运行状态变成就绪状态
//让main线程彻底放弃CPU的使用权,就是让其阻塞,处于阻塞状态
try {
Thread.sleep(1);
}catch (Exception e){
e.printStackTrace();
}
}
}
}
private static class ThreadTask implements Runnable{
@Override
public void run() {
for(int i = 0; i < 10; i++){
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
}
}
}
子线程需要调用start()方法才会就绪,但主线程是一进来就已是就绪了的。一般子线程执行完后就会自己自动死亡
控制线程
线程休眠
Thread类提供了休眠方法,可以让当前线程暂停一段时间
static void sleep(long millis)
static void sleep(long millis, int nanos)
sleep() vs yield()
- sleep()会让线程进入阻塞状态,而yield()会让线程进入就绪状态
- sleep()给其他线程运行的机会,不理会其他线程的优先级,yield()考虑线程的优先级,只会给优先级相同,或优先级更高的线程执行机会
- sleep更可控,yield不可控
等待线程 join
Thread类提供了等待方法,可以让调用方等待该线程直至它死亡
void main(String[] args){
Thread thread = ...;
thread.start();
thread.join(); //...
}
main是调用方,会一直等待thread方法执行结束,并死亡
示例
public class ThreadDemo3 {
public static void main(String[] args){
testJoinThread();
}
private static class JoinThreadTask implements Runnable{
@Override
public void run() {
for(int i = 0; i < 30; i++){
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
}
}
public static void testJoinThread(){
new Thread(new JoinThreadTask()).start();
for(int i = 0; i < 30; i++){
System.out.println(Thread.currentThread().getName() + "\t\t" + i);
if(i == 10){
Thread thread = new Thread(new JoinThreadTask());
thread.start();
try {
thread.join();//让main线程等待thread-1执行完毕后才会再执行
}catch (Exception e){
e.printStackTrace();
}
}
}
}
}
后台线程
- 后台线程,也叫守护线程,或精灵线程
- 它是在后台运行的,它的任务是为其他线程提供服务
- 如果所有的前台线程都死亡,则后台线程会自动死亡
- 线程默认是前台线程,Thread类提供如下方法来设置后台线程
void setDaemon(boolean on)
boolean isDaemon()
前台线程都死亡后,JVM会通知后台线程死亡,但从它接收指令到做出响应,需要一定的时间。
示例
private static class DamonThreadTask implements Runnable{
@Override
public void run() {
for(int i = 0; i < 100; i++){
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
}
}
public static void testDemoThread(){
Thread thread = new Thread(new DamonThreadTask());
//注意是先设置为daemon线程然后再start
//和之前的先start()再join()方法有所区别
thread.setDaemon(true);
thread.start();
//让main线程打印
for(int i = 0; i <10; i++){
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
}
- main线程默认是前台线程,因为main线程先结束,后台线程也跟着结束,后台线程打印从0-100,main线程打印从0-10,结果发现只要main打印完9,daemon线程也停止了执行(自动死亡,还有会一定的延迟)
线程优先级
- 线程运行时拥有优先级,优先级高的线程则拥有较多的运行机会
- 线程默认的优先级与它的父线程相同,而主线程具有普通优先级
- Thread类提供了如下的成员,来处理线程的优先级;
static int MAX_PRIORITY;// 10
static int MIN_PRIORITY; // 1
static int NORM_PRIORITY; // 5
int getPriority()
void setPriority(int newPriority)
- 线程的优先级需要操作系统的支持,虽然Java支持10种优先级,但是操作系统支持的优先级可能少于10种,所以最好不要通过数字设置线程的优先级,而是尽可能地采用静态变量来指定。
private static class PriorityThreadTask implements Runnable{
@Override
public void run() {
for(int i = 0; i < 10000; i++){
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
}
}
public static void testPriorityThread(){
Thread thread0 = new Thread(new PriorityThreadTask());
Thread thread1 = new Thread(new PriorityThreadTask());
//打印main线程的优先级
System.out.println(Thread.currentThread().getPriority());
System.out.println(thread0.getPriority());
System.out.println(thread1.getPriority());
thread0.setPriority(Thread.MAX_PRIORITY);
thread0.start();
thread1.setPriority(Thread.MIN_PRIORITY);
thread1.start();
}
- main线程和创建的新线程的优先级为5,而且通过将thread-0的优先级设置为最大,thread-1的优先级设置为最小,最后结果显示,thread-0先执行完毕。