多线程-1-多线程知识体系及线程生命周期

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weinabanta/article/details/28854551

多线程知识体系


多线程的生命周期可以用下图来表示,包含了多线程所有模块。


0.线程生命周期知识体系

 线程生命周期对于掌握真正掌握多线程是非常重要的,只有掌握它才能对线程的整个运作有非常明确的认识,在真正使用时,才会做到不糊涂。以下是我制作的线程状态转换图,本文的所有论述都是根据此图展开。

当线程被创建并启动以后,既不是一启动就进入执行状态,也不是一直处于执行状态,在线程的生命周期中一共有5大状态,会经历新建、就绪、运行、阻塞、死亡5种状态,并且还要在就绪、运行、阻塞3种状态不断进行状态转换,因为CPU需要在多条线程之间切换。


1.新建状态

当程序通过new关键字创建线程(3种方式)后,该线程就处于新建状态,与其它普通Java对象一样,仅仅由Java虚拟机为其分配内存,并初始化成员变量,此时的线程对象没有表现任何线程的动态特征,程序也不会执行线程的线程执行体。


2.就绪状态

当线程对象调用了start()方法之后,该线程就处于就绪状态,Java虚拟机会为其分配方法栈和程序计数器,此状态的线程并没有运行,而只是具体了运行资格可以运行,即被CPU调度,至于什么时候开始运行就取决于JVM线程调度器的调度。

注:
1.启动线程使用start()方法,而不是run()方法,永远不要调用线程对象的run()方法。调用start()方法启动线程,系统会把run()方法当成线程执行体处理;但如果直接调用run()方法,其会立即执行,系统会把线程对象当成普通对象处理,run()也是一个普通方法而不是线程执行体;当调用了start()方法后,该线程已经不处于新建状态,不能再次调用其start()方法。
2.只能对处于新建状态的线程调用start()方法,否则将引发IllegalThreadStateException异常。

如下代码:

class NewState extends Thread{
	public void run(){
		for(int i = 0; i < 23; i++){
			System.out.println(Thread.currentThread().getName() + "变量 i = " + i);
		}
	}
}
public class NewStateTest {
	public static void main(String[] args) {
		System.out.println("主线程开始");
		//新建状态
		NewState ns = new NewState();
		//直接run()方法,输出结果表明是main主线程在执行。
		ns.run();
		ns.start();
		//ns.start();
		System.out.println("主线程结束");
	}
}

调用了线程对象的start()方法后,该线程立即进入就绪状态,但并没有真正进入执行状态,以上代码也可以看出。如果想要立即执行,只要让主线程sleep()一下就行,此可以由上方的状态转换图印证,主线程调用sleep()方法,其实就是由运行状态转换为阻塞状态,CPU就会调用其它就绪状态的线程,此处只有两个线程,调用的当然是ns线程。如下代码:

class NewState extends Thread{
	public void run(){
		for(int i = 0; i < 23; i++){
			System.out.println(Thread.currentThread().getName() + "变量 i = " + i);
		}
	}
}
public class NewStateTest {
	public static void main(String[] args) {
		System.out.println("主线程开始");
		//新建状态
		NewState ns = new NewState();
		//转为就绪状态
		ns.start();
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("主线程结束");
	}
}


3.运行状态

处于就绪状态的线程获得了CPU,开始执行run()方法的线程执行体(此处由start()源码可以看出start调用了run()方法),则该线程处于运行状态,如果计算机只有一个CPU,那么任何时刻只有一个线程处于运行状态。在一个多处理器的机器上,将会有多个线程并行(注意是并行而不是并发);当线程数大于处理器数时,依然会存在多个线程在同一个CPU上轮换的现象。


4.阻塞状态

当一个线程开始运行后,不可能一直处于运行状态(除非它的线程执行体足够短,瞬间就执行结束了),线程在运行过程中需要被中断,目的是使其他线程获得执行的机会,线程调度的细节取决于底层平台采用的调用策略。
下面对操作系统的调度策略说明:

1)抢占式策略操作系统

系统给每个可执行的线程一个时间片(一小段时间)来处理任务,当该时间片用完之后,系统就会剥夺该线程所占用的资源,让其他线程获得执行的机会。在选择下一个线程进行调度时,系统会考虑线程的优先级。所有现代的桌面或服务器操作系统都采用抢占式调度策略。

2)协作式策略操作系统

在协作式策略操作系统中,当一个线程调用了它的sleep()或yield() 方法后才会放弃所占用的资源,也就是必须由该线程主动放弃所占用的资源。


1)进入阻塞状态

发生如下情况时,线程就会进入阻塞状态,上图也有
(1)线程调用sleep()方法,主动放弃所占用的处理器资源。
(2)线程调用了阻塞式IO方法,在该方法返回之前,该线程就会被阻塞。
(3)线程试图获取同步监视器,但该监视器正被其它线程持有。
(4)线程在等待某个通知(notify()或notifyAll())。
(5)程序调用了线程的suspend()方法将该线程挂起。但此方法容易导致死锁,尽量避免使用该方法。

当前正在执行的线程被阻塞之后,其他线程就可以获得执行机会。被阻塞的线程会在合适的时候进入就绪状态(是就绪状态,而不是运行状态,此处由上面的图可以看出)。被阻塞线程的阻塞解除后,必须重新等待线程调度器再次调度。


2)退出阻塞状态

当前处于阻塞状态的线程在发生下列事情后,会重新进入就绪状态。
(1)调用sleep()方法的线程经过指定时间返回。
(2)线程调用阻塞式IO方法已经返回。
(3)线程成功获取了试图取得的监视器。
(4)线程正在等待的通知到来。

(5)处于挂起状态的线程被调用了resume()恢复方法。


5.死亡状态

以下3种方式会结束线程,结束后的线程就处于死亡状态。
(1)run()或call()方法执行完成。
(2)线程抛出一个未捕获的Exception或Error
(3)直接调用线程的stop()方法结束该线程,该方法容易导致死锁。
isAlive()方法可以测试某个线程是否死亡,当线程处于就绪、运行、阻塞3种状态时,该方法返回true;当线程处于新建、死亡2种状态时,该方法返回false。
 注:
(1)当主线程结束后,其他线程不受影响,并不会随之结束,因为他与主线程永有相同的地位,不会受到主线程的影响。只要有一个线程在执行,程序就不会结束;
(2)不要试图对一个已经死亡的线程调用start()方法使其重新启动,因为死不复生,否则会引发IllegalThreadStateException。
class NewState extends Thread{
	@SuppressWarnings("deprecation")
	public void run(){
		for(int i = 0; i < 23; i++){
			System.out.println(Thread.currentThread().getName() + "变量 i = " + i);
			if(i == 10){
				//当i=10时,stop线程
				stop();
			}
		}
	}
}
public class NewStateTest {
	public static void main(String[] args) {
		NewState ns = new NewState();
		ns.start();
		ns.start();
	}
}

猜你喜欢

转载自blog.csdn.net/weinabanta/article/details/28854551