线程的生命周期:一个线程从创建到消亡的过程
如下图,表示线程生命周期中的各个状态:
线程的生命周期可以分为五个状态:
1.创建状态:
当用new操作符创建一个新的线程对象时,该线程处于创建状态。
处于创建状态的线程只是一个空的线程对象,系统不为它分配资源。
2.可运行状态【runnable】:
执行线程的start()方法将为线程分配必须的系统资源,安排其运行,并调用线程体——run()方法,
这样就使得该线程处于可运行状态(Runnable)。
这一状态并不是运行中状态(Running),因为线程也许实际上并未真正运行。
注意:一个线程只有在start()之后才是具备可执行能力的(runnable状态的),但是具备可执行能力
并不代表着会立即执行(running)一个线程,线程会等待着cpu的调度,只有在cpu调度了该
线程之后,该线程才会执行!换句话说:runnable状态的线程可能会立即执行,也可能不会
立即执行,只有当runnable状态的线程获得cpu调度执行权的时候才会立即执行!
3.正在执行状态【running】:
需要注意的是:线程是被cpu调度的,多个线程或者多个进程之间的切换是由cpu来完成的!
4.阻塞状态:
当发生下列事件时,处于运行状态的线程会转入到不可运行状态【阻塞状态】:
调用了sleep()方法;
线程调用wait()方法等待特定条件的满足;
线程输入/输出阻塞。
或者线程在抢锁的时候
执行了yield()方法
返回可运行状态:
处于睡眠状态的线程在指定的时间过去后;
如果线程在等待某一条件,另一个对象必须通过notify()或notifyAll()方法通知等待线程条件的改变;
如果线程是因为输入输出阻塞,等待输入输出完成。
注意:1.当我们调用Thread.sleep()方法、wait()方法、yield()方法,也或者是争抢锁资源的时候 线程就会由running状态转到blocked【阻
塞状态】,而一个线程进入阻塞【blocked】, 状态之后,是不能直接恢复到running状态的,它是需要先进入!
2.另外running状态的线程除了可以通过blocked状态进入runnnable状态的情况外,处于running状态的线程还可以直接进入到runnable
状态,这是因为cpu调度将执行权交给了其它线程了,多个线程或者多个进程之间的切换是由cpu来完成的!只不过速度很快,你看
不出来而已,但是在某一个具体的时间点cpu只能执行一个线程!
5.消亡状态:
当线程的run()方法执行结束后,该线程自然消亡。
注意:最后一个状态是终结状态[disabled或者terminated]:当线程正常执行完毕或者也可能是从blocked状态的线程被打断的时候【比如
sleep()、wait()、抢锁的时候被打断了】就会进入这种终结状态,也有可能由于操作系统的原因,线程会直接从runnable状态进入
到terminated状态!
public class MultiThread { public static void main(String[] args) { new Thread("READ-THREAD"){ @Override public void run() { System.out.println(Thread.currentThread().getName()); readFromDataBase(); } }.start(); new Thread("WRITE-THREAD"){ @Override public void run() { writeDataToFile(); } }.start(); } private static void readFromDataBase() { //read data from database and handle it try { println("begin read data from db."); Thread.sleep(1000*30L); println("read data done and handle it now"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } println("the data handle finish and successfully."); } private static void writeDataToFile(){ //write data to file try { println("begin write data to file."); Thread.sleep(1000*30L); println("write data done and start handle it now"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } println("the data handle finish and successfully."); } private static void println(String message){ System.out.println(message); } }
针对上面的代码,这里需要注意的是我们直接用匿名类对象直接调用的是start()方法,然后JVM虚拟机就会帮我们调用线程的run()方法,所以才会在
System.out.println(Thread.currentThread().getName());这段代码这里输出了READ-THREAD !如果我们不是用匿名对象,而是创建一个普通的对象,然后用 普通对象直接调用run()方法,也就如下:
Thread t = new Thread("READ-THREAD"){ @Override public void run() { System.out.println(Thread.currentThread().getName()); readFromDataBase(); } }; //t.start() t.run();
显然这是不可以的,因为如果我们直接用线程对象调用run()方法,而不是调用start()方法,那么此时该线程就相当于没启动,此时
System.out.println(Thread.currentThread().getName());输出的将不再是READ-THREAD,而是main线程了!切记切记!
另外,我们这里也总结一下其它的:
1.我们知道,进程中包括线程,线程才是真正干事的,也就是说一个进程中至少有一个线程(main线程或者主线程),这个线程是被JVM调用的,而且线程的名字就是main!
2.实现一个线程,必须创建Thread实例,覆写里面的run方法,并且调用start()方法!这里需要注意:如果我们不调用run方法,那么这个类就是一个普通的实例类,不是多线程!
3.当调用了一个线程的start()方法之后,此时至少存在两个线程,一个是调用线程的main线程【也就是调用线程的线程】,再一个是这个线程!
4.线程的生命周期分为:new ,runnable,running,block,terminate等几部分!