线程生命周期及五种状态

时间:2022-09-15 01:24:51

一、线程生命周期及五种状态

线程生命周期及五种状态

1、New(初始化状态)

用new语句创建的线程处于新建状态,此时它和其他Java对象一样,仅仅在堆区中被分配了内存。如:Thread t = new MyThread();

2、Runnable(就绪状态)

当调用线程对象的start()方法,线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,Java虚拟机会为它创建方法调用栈和程序计数器。处于这个状态的线程位于可运行池中,等待获得CPU的使用权,并不是说执行了start()此线程立即就会执行。

3、Running(运行状态)

当就绪状态中的线程获得了CUP执行资源,执行run()中的代码,这样的线程我们称为运行状态的线程。

4、Blocked(阻塞状态)

处于运行中的线程,由于某种原因放弃对cpu的使用权,处于阻塞状态,直到其进入就绪状态,才有机会再次被cpu调用进入运行状态。

根据阻塞原因不同,阻塞分为三种

  • 等待阻塞:运行状态中的线程执行wait方法,进入等待队列,等待阻塞;Java虚拟机就会把线程放到这个对象的等待池中;

  • 同步阻塞:线程获取同步锁失败(因为锁被其他线程占用),Java虚拟机就会把这个线程放到这个对象的锁池中;

  • 其他阻塞:通过调用sleep方法或者join方法或者发出I/O请求时,线程会进入阻塞状态,当sleep()状态超时,或者join()等待线程终止或者超时,或者I/O处理完毕,线程重新转入就绪状态;

5、Terminated(终止状态)

  • 正常结束,线程执行完

  • 异常退出
    异常退出,除了程序有问题导致的异常的退出,还可以使用共享变量的方式(定义个boolean标识等)退出,或者Interrupt中断线程,抛出异常,捕获异常break,跳出循环状态;

  • 调用stop(),会造成死锁,线程不安全,不建议使用

二、线程基本方法

1、线程等待(wait)

调用该方法,线程进入waiting状态,只有等待另外的线程通知或被中断才会返回,调用wait()后,会释放对象锁,因为wait方法一般用在同步方法或者同步代码块中。

2、线程睡眠(sleep)

强迫一线程睡 N毫秒,sleep不会释放当前锁,导致线程进入Timed-wating状态。

3、线程让步(yield)

yeild会使当前线程让出cpu执行时间片,与其他线程一起重新竞争cpu时间片,一般情况下,优先级高的先得到,但也不一定,有的系统对优先级不敏感。

4、线程中断(interrupt)

调用一个线程的interrupt() 方法中断一个线程,并不是强行关闭这个线程,仅仅是改变了内部维护的中断标识位,是线程固有的一个标识位。可以调用
在run内部根据thread.isIterrupted() 安全终止线程。

static方法isIterrupted() 判定当前线程是否处于中断状态:
1)如果一个正常线程,调用interrupt() ,是不能被打断的,打印中断标志位置为true;

public class TestIterruptedDemo1{
	 public static void main (string[] args)throws InterruptedException {
			//创建线程t1
			 Thread  t1=new Thread (()->
				 if(Thread.currentThread.isInterrupted()){
				   log.dubug("---------t1中断标识true,进来了---------");   
				 }
			},name:"t1");
			t1.start();
			t1.interrupt();//没等t1启动,主线程先执行打断t1,将表示设置为true		
		}
}
t1还没有启动,主线程便执行了一个打断,即打断一个正常线程,则打印结果为true,将线程标记设置为true

控制台打印:

---------t1中断标识true,进来了---------"

2)如果一个sleep或者wait的线程,调用interrupt() ,方法则抛出InterruptedException( InterruptedException表示一个阻塞被中断了),线程的中断标志位会被复位成false;相当于用异常响应了这个中断,所以释放中断标志位。

public class TestIterruptedDemo2{
	 public static void main (string[] args)throws InterruptedException {
			//创建线程t1
			 Thread  t1=new Thread (()->
			 try {
	            log.dubug("---------t1启动,然后sleep---------");   
	            TimeUnit.SECONDS.sleep(timeout:200); 
	        } catch (InterruptedException e) {
	        	log.dubug("---------t1被主线程打断了---------");   
	            e.printStackTrace();
	        } 
			},name:"t1");
			t1.start();
			//主线程睡一下,主要是为了确保t1先执行,然后再打断sleep的t1
			TimeUnit.SECONDS.sleep(timeout:1);  
			t1.interrupt();//主线程1秒后醒来,打断t1		
			log.dubug("---------t1的打断标识是{}---------",t1.isInterrupted());   

		}
}

控制台打印:

---------t1启动,然后sleep---------
---------t1被主线程打断了---------
---------t1的打断标识是false---------

5、join(等待其他线程终止)

当前线程调用join(),则线程转为阻塞状态,eg:A线程中插入了B.join(),则B先执行,执行完,A线程继续执行;常见的是主线程生成并启动了子线程,需要用到子线程返回结果的场景;

6、线程唤醒(notify)

Object类中的notify唤醒在此对象监视器上等待的单个线程;
notifyAll唤醒在此对象监视器上等待的所有线程;

7、其他方法

  • isAlive判断一个线程是否存活;
  • activeCount 程序中活跃的线程数;
  • currentThread得到当前线程;
  • setPriority设置一个线程的优先级;
  • getPriority获取一个线程的优先级;
  • isDaemon线程是否为守护线程;

小结:方法wait(), notify()和notifyAll()被定义在Object 类里;
Thread类的sleep()和yield ()方法是静态的;
wait(), notify()和notifyAll ()必须在同步方法或者同步块中被调用

三、重点面试问题

1、sleep和wait的区别:

sleep属于Thread类,让出cpu,但是监控状态依然保持者,指定时间到了,就会恢复运行;
wait属于Object类方法,释放对象锁,进入等待锁定池,需要notify()才能重新进入运行状态;

2、wait()、notify() 释放锁问题:

wait()会立刻释放synchronized(obj)中的obj锁,以便其他线程可以执行obj.notify()

但是notify()不会立刻立刻释放sycronized(obj)中的obj锁,必须要等notify()所在线程执行完synchronized(obj)块中的所有代码才会释放这把锁.yield(),sleep()不会释放锁。

线程生命周期及五种状态