首页 > 代码库 > java多线程学习(三)——线程栈

java多线程学习(三)——线程栈

一、线程栈模型

线程栈模型是理解线程调度原理以及线程执行过程的基础。线程栈是指某时刻时内存中线程调度的栈信息,当前调用的方法总是位于栈顶,线程栈的内容是随着线程的运行状态变化而变化的,研究线程栈必须选择一个运行的时刻(指代码运行到什么地方)


上图中的栈A是主线程main的运行栈信息,当执行new JavaThreadDemo().threadMethod();方法时,threadMethod方法位于主线程栈中的栈顶,在threadMethod方法中运行的start()方法新建立了一个线程,此时,新建立的线程也将拥有自己的线程栈B,可以看到在不同运行时刻栈的信息在发生变化,栈A的变化可以从上图中看出来。此时栈A和栈B并行运行,main线程和新建立的线程并行运行。由此可以看出方法调用和线程启动的区别。方法调用只是在原来的线程栈中调用方法的代码,而线程启动会新建立一个独属于自己的线程栈来运行自己的这个线程。

二、线程的生命周期

线程的生命周期包括五种状态:新建、可运行状态、运行状态、阻塞、死亡。


新建:创建了一个新的线程对象,但是还没有调用线程中start()方法,此时线程处于新建状态。

可运行状态:调用线程的start()方法,线程进入可运行状态,此时线程等待着JVM的调度程序来执行,就是说我已经准备好了,可以上战场了,只等待一个机会就可以变为运行状态了。

运行状态:线程调度程序从众多的可运行状态线程中选择一个线程来运行,好比主帅选择一个大将来上阵较量。这也是线程进入运行状态的唯一方式,必须由JVM来调度。

阻塞状态:线程的等待、睡眠和阻塞统称为阻塞状态,此时线程依然是活得,处于待命状态,知道某个条件出现时,即可返回可运行状态。

死亡状态:当线程的run()方法执行完毕后,线程即结束。此时线程已经不在存在,它所占用的所有资源都会被回收。


三、线程阻塞

线程的阻塞有多种,常见的包括三项(IO阻塞不讨论)

1、睡眠

2、等待

3、获取线程锁而阻塞

睡眠状态:线程的sleep()方法可以使一个正在执行的线程进入睡眠状态。线程睡眠时,不会返回可运行状态,当睡眠时间到时才返回可运行状态。

@Override
	public void run() {
		for(int i = 0; i < 5; i++) {
			try {
				Thread.sleep(50);//模拟耗时操作
				System.out.println(name + ":" + i);
				
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
	}
调用线程的静态方法Thread.sleep()可以使线程睡眠,参数为毫秒。

1、线程处于睡眠状态时,JVM调度程序会暂停此线程的执行并来执行其他的处于可运行状态的线程。

2、sleep()方法是Thread的静态方法,只能控制当前线程的睡眠。

3、线程睡眠时间到之后会返回到可运行状态,而不是运行状态。

线程的优先级和线程让步yield()

线程通过调用Thread.yield()方法来实现线程的让步。yield()方法的作用为暂停当前正在执行的线程对象,让出处理器资源,让处理器来执行其他线程。

Java中的线程存在优先级,优先级的范围为1——10,之间。JVM的线程调度程序是基于优先级的抢先调度机制。高优先级的线程比低优先级的线程有更高的几率得到执行,只是几率高一点,并不是说低优先级的线程必须等待高优先级的线程执行完成后才开始执行,只是得到的资源分配的比例不一样而已。(线程优先级在实际应用中并不能按你预期的来执行)

当线程池中的线程都具有相同的优先级时,JVM的调度程序随机选择处于可运行状态的线程来执行,此时可能有两种执行过程:(1)选择一个线程运行,直到此线程阻塞或者运行到死亡。(2)时间分片机制,为线程池中的每个线程都提供均匀的运行机会。

设置线程的优先级可以通过调用线程的setPriority(int)方法来设置线程的优先级。默认的线程优先级为5,JVM不会随便更改一个线程的优先级,但是不同系统中的线程优先级的范围可能不同,当JVM不能识别10个不同的优先级时,JVM会将这些优先级进行两个或者多个的合并,此时合并前的不同优先级的线程最后会被合并成一个优先级的线程。

Thread类中定义了三个常量来定义优先级的范围:

static int MAX_PRIORITY 线程具有的最大优先级

static int MIN_PRIORITY  线程具有的最小优先级

static int NORM_PRIORITY 线程具有的默认优先级


使线程让步的yield()方法

Thread.yield()方法可以暂停当前正在执行的线程,使处理器来有机会执行其他线程,即可以让当前线程做出让步,也让其他线程有机会被执行。线程执行yield()方法后,线程就从运行状态回到了可运行状态,使具有相同优先级的线程获得机会来执行。使用yield()方法可以做到在相同优先级的线程之间适当的轮换执行。但是,实际中无法保证yield()方法达到让步,因为让步的线程从运行状态回到了可运行状态,那么此线程还可以被JVM的调度机制来选中来执行。


join方法

Thread的非静态方法join()让一个线程B加入到另一个线程A的尾部,直到线程A执行完毕后,线程B才能继续执行。这相当于打断线程B的执行来先执行了线程A,类似与在main()函数中调用了另一个函数,直到此函数执行完毕后,main()函数才继续执行。

join的方法中有一个带时间参数的版本,t.join(5000)让线程等待5000毫秒,如果超过这个时间,则停止等待,变为可运行状态。


让线程暂时离开运行状态的三中方法:

1、调用线程的sleep()方法,使线程睡眠一段时间

2、调用线程的yield()方法,使线程暂时回到可运行状态,来使其他线程有机会执行。

3、调用线程的join()方法,使当前线程停止执行,知道当前线程中加入的线程执行完毕后,当前线程才可以执行。






java多线程学习(三)——线程栈