一.生命周期流转图
- 线程对象的状态存放在Thread类的内部类中(State)中:
注意:Thread.State类其实是一个枚举类,因为线程对象的状态是拱顶的只有6种
- NEW
至今尚未启动的线程处于这种状态。
- RUNNABLE
正在 Java 虚拟机中执行的线程处于这种状态。
- BLOCKED
受阻塞并等待某个监视器锁的线程处于这种状态。
- WAITING
无限期地等待另一个线程来执行某一特定操作的线程处于这种状态。
- TIMED_WAITING
等待另一个线程来执行取决于指定等待时间的操作的线程处于这种状态。
- TERMINATED
已退出的线程处于这种状态。
在给定时间点上,一个线程只能处于一种状态。这些状态是虚拟机状态,它们并没有反映所有操作系统线程状态。
- 各个状态的分析:
-
新建状态(new)
使用new创建一个线程对象,仅仅在堆中分配内存空间,在调用start方法之前.
新建状态下,线程压根就没有启动,仅仅只是一个存在一个线程对象而已.
Thread t = new Thread();//此时就属于新建状态
当新建状态下的线程对象调用了start方法,此时从新建状态进入可运行状态
线程对象的start方法只能调用一次,否则报错.IllegalThreadStateException -
可运行状态(runnable)
分成两种状态,ready和running.分别表示就绪状态和运行状态.
就绪状态:线程对象调用start方法之后,等待JVM的调度(此时该线程并没有运行)
运行状态:线程对象获得JVM调用,如果存在多个CPU,那么允许多个线程并行运行.
-
阻塞状态(blocked)
正在运行的线程因为某些原因放弃CPU,暂时停止运行,就会进入阻塞状态,
此时JVM不会给线程分配CPU直到线程重新进入就绪状态,才有机会转到运行状态.
阻塞状态只能先进入就绪状态,不能直接进入运行状态.
阻塞状态的两种情况:- 当A线程处于运行过程时,试图获取同步锁时,却被B线程获取,此时JVM把当前的A线程
存到对象的锁池中,A线程进入阻塞状态. - 当线程处于运行过程时,发出了IO请求,此时进入阻塞状态.
- 当A线程处于运行过程时,试图获取同步锁时,却被B线程获取,此时JVM把当前的A线程
-
运行状态(waiting)(只能被其他线程唤醒):
当线程处于运行过程时,调用了wait()方法,此时JVM把当前线程存在对象等待池中
当线程执行了sleep()方法. -
等待状态(timed waiting)
当线程处于运行过程时,调用了wait(long time)方法,此时JVM把当前线程存在对象等待池中,
当线程执行了sleep(long time)方法 -
终止状态(terminated)
通常称之为死亡状态,表示线程终止,- 正常执行完run方法而退出(正常死亡)
- 遇到异常而退出(出现异常之后,程序就会中断)(意外死亡)
线程一旦终止,就不能唤醒了,否则报错.IllegalThreadStateException
在Thread类中,过时的方法(因为存在线程安全问题,所以弃用了): void suspend():暂停当前线程 void resume():恢复当前线程 void stop():结束当前线程
-
有人把可运行,等待,计时等待合称为阻塞状态
二.线程睡眠
- 让执行的线程暂停一段时间,进入计时等待状态
- 方法:static void sleep(long millis)
调用sleep后,当前线程放弃CPU,在指定时间段之内,sleep所在线程不会获得执行的机会,
此状态下的线程不会释放同步锁/同步监听器 - 该方法更多的用于模拟网络延迟,让多线程并发访问一个资源的错误效果更佳明显.
新建SleepDemo.java
public class SleepDemo {
public static void main(String[] args) {
for (int i = 3; i > 0; i--) {
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("boon!");
}
}
三.联合线程
- 线程的join方法表示一个线程等待另一个线程完成后才执行,join方法被调用之后,线程对象处于阻塞状态,
也有人把这种方式称之为联合线程,就是说把当前线程和当前线程所在的线程联合成一个线程
新建JoinDemo.java
//join联合线程
class Join extends Thread{
public void run() {
for(int i = 0;i < 50; i ++) {
System.out.println("Join:" + i);
}
}
}
public class JoinDemo {
public static void main(String[] args) {
Join j = new Join();
for (int i = 0; i < 50; i++) {
System.out.println("main:" + i);
if(i == 10) {
j.start();
}
if (i == 30) {
try {
j.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
![image][unite]
四.后台线程
- 在后台运行的线程,其目的是为其他线程提供服务,也称之为"守护线程".JVM的垃圾回收线程就是典型的
后台线程 - 特点:若所有的前台线程都死亡,后台线程自动死亡,前台线程没有结束,后台线程也不会结束.
- 测试线程对象是否为后台线程:使用thread.isDaemon().
其他线程创建的线程默认是前台线程,可以通过setDaemon方法设置为后台线程,并且当且仅当后台线程
创建的新线程时,新线程是后台线程 - 设置后台线程:thread.setDaemon(true),该方法必须在start方法前调用,否则出现
IllegalThreadStateException异常
新建DaeDmonDemo.java
//DaeDmon后台线程
class DaeDmon extends Thread{
@Override
public void run() {
super.run();
for (int i = 0; i < 100; i++) {
System.out.println(this.getName() + i + this.isDaemon());
}
}
}
public class DaeDmonDemo {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName());
System.out.println(Thread.currentThread().isDaemon());
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName() + i + Thread.currentThread().isDaemon());
if (i == 10) {
DaeDmon dd = new DaeDmon();
dd.setDaemon(true);
dd.start();
}
}
}
}
五.线程优先级
- 每个线程都有优先级,优先级的高低和线程获得执行机会的次数多少有关,并非线程优先级越高的就一定
会先执行,哪个线程的先运行取决于CPU的调度
MAX_PRIORITY = 10;最高优先级
MIN_PRIORITY = 1;最低优先级
NORM_PRIORITY = 5;默认优先级
int getPriority();返回线程的优先级
void setPriority();更改线程的优先级
- 每个线程都有默认优先级,主线程默认优先级为5,如果A线程创建了B线程,那么B线程和A线程拥有相
同的优先级.
注意:不同的操作系统支持的线程优先级不同,建议使用上述三个优先级,不要自定义.
新建PriorityDemo.java
//线程优先级问题Priority
class Priority extends Thread{
Priority(String name){
super(name);
}
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println("第" + i + "个" + this.getName() + this.getPriority());
}
}
}
public class ThreadPriorityDemo {
public static void main(String[] args) {
Priority min1 = new Priority("低优先级");
Priority min2 = new Priority("低优先级");
Priority min3 = new Priority("低优先级");
Priority min4 = new Priority("低优先级");
min1.setPriority(min1.MIN_PRIORITY);
min2.setPriority(min1.MIN_PRIORITY);
min3.setPriority(min1.MIN_PRIORITY);
min4.setPriority(min1.MIN_PRIORITY);
Priority max1 = new Priority("高------优先级");
Priority max2 = new Priority("高------优先级");
Priority max3 = new Priority("高------优先级");
Priority max4 = new Priority("高------优先级");
max1.setPriority(max1.MAX_PRIORITY);
max2.setPriority(max1.MAX_PRIORITY);
max3.setPriority(max1.MAX_PRIORITY);
max4.setPriority(max1.MAX_PRIORITY);
min1.start();
min2.start();
min3.start();
min4.start();
max1.start();
max2.start();
max3.start();
max4.start();
}
}
六.线程礼让
-
yield方法:表示当前线程对象提示调度器自己愿意让出CPU,但是调度器可以自由的忽略该提示,
-
调用该方法之后,线程对象进入就绪状态,所以完全有可能:某个线程调用yield()后,线程调度器又把
它调度出来执行. -
从Jvav7提供的文档中可以清楚看出,开发中很少会使用到该方法,该方法主要用于调试或测试,它可能有
助于因多线程竞争条件下的错误重现现象 -
sleep方法和yield方法的区别:
- 都能使当前处于运行状态的线程放弃CPU,把运行的机会给其他线程
- sleep方法会给其他线程运行机会,但是不考虑其他线程的优先级,yield方法只会给相同优先级
或者更高优先级的线程运行机会 - 调用sleep方法之后,线程进入计时等待状态,调用yield方法后,线程进入就绪状态.