线程具有生命周期,其中包含5种状态,分别为出生状态、就绪状态、运行状态、暂停状态(包括休眠、等待和阻塞等)和死亡状态。
一、五种状态讲解
出生状态:就是线程被创建时的状态;
就绪状态:线程对象调用start()方法后,线程处于就绪状态(又称为可执行状态);
运行状态:当线程得到系统资源后就进入了运行状态,一旦进入运行状态,他会在就绪与运行状态下转换,同时也有可能进入暂停或死亡状态;
暂停(阻塞)状态:当线程调用sleep()方法、wait()方法或者发生阻塞时会进入暂停状态。当线程休眠结束、调用notify或notifyAll方法、或者阻塞结束时候,线程会重新进入就绪状态;
死亡状态;run方法执行完毕,或者发生错误异常时进入死亡状态,一旦进入死亡状态就不能再次启动;
二、操作线程的方法
操作线程有很多方法,这些方法可以使线程从某一种状态过渡到另一种状态。
1、线程的休眠
sleep()方法可以指定线程休眠的时间,线程休眠的时间以毫秒为单位。
由于sleep()方法有可能抛出InterruptedException异常,所以将sleep()方法放在try-catch块中。虽然sleep()方法的线程在一段时间会醒来,但是并不能保证他醒来后就会进入运行状态,只能保证他进入就绪状态。
代码示例:
利用sleep输出时间到秒
public static void main(String[] args) {
Date startTime = new Date(System.currentTimeMillis());//获取系统当前时间
while(true){
try {
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
startTime = new Date(System.currentTimeMillis());//更新当前时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
输出结果:
2、线程的加入
假如当前程序为多线程程序,并且存在一个线程A,现在需要插入B线程,并要求B线程执行完之后再执行A,此时可以用join()方法来实现。这就好比A正在看电视,突然B上门收水费,A必须付完水费后才能继续看电视。
代码示例:
//join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞
public class TestJoin implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("线程VIP来了"+i);
}
}
public static void main(String[] args) throws InterruptedException {
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);
thread.start();
for (int i = 0; i < 500; i++) {
if(i == 200) {
thread.join();//插队
}
System.out.println("main"+i);
}
}
}
输出结果:
3、线程的中断
以往会使用stop()方法停止线程,但是JDK早已废除了stop()方法,不建议使用stop()方法来停止线程。
这是我专门写的一篇关于线程停止的博客。
链接: https://blog.csdn.net/weixin_43888891/article/details/115291302
4、线程礼让
礼让线程,让当前正在执行的线程暂停,但不阻塞。
将线程从运行状态转为就绪状态。
让cpu重新调度,礼让不一定成功,看cpu心情。
礼让是调用Thread对象的yield()方法。
代码示例:
public class TestYield {
public static void main(String[] args) {
MyYield myYield = new MyYield();
new Thread(myYield,"a").start();
new Thread(myYield,"b").start();
}
}
class MyYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程执行开始");
Thread.yield(); //礼让
System.out.println(Thread.currentThread().getName()+"线程执行停止");
}
}
输出结果:
5、观察线程状态
Thread对象的getState()方法可以查看线程状态。
*代码示例
public class TestState {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
System.out.println(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
//观察状态
Thread.State state = thread.getState();
System.out.println(state);
//观察启动后
thread.start();
state = thread.getState();
System.out.println(state);
//只要线程不终止,就一直输出
while (state != Thread.State.TERMINATED) {
Thread.sleep(100);
state = thread.getState();
System.out.println(state);
}
}
}
输出结果:
6、设置线程优先级
Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程
线程调度器按照优先级决定应该调度哪个线程来执行
线程优先级用数字表示,范围从1-10
优先级低只是意味着获得调度的概率低,并不是优先级低就不会被调用了,这都是看CPU的调度
Thread对象的setPriority(1)方法可以设置线程优先级
代码示例:
public class TestPriority {
public static void main(String[] args) {
//主线程默认优先级
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
MyPriority priority = new MyPriority();
Thread t1 = new Thread(priority);
Thread t2 = new Thread(priority);
Thread t3 = new Thread(priority);
Thread t4 = new Thread(priority);
Thread t5 = new Thread(priority);
//先设置优先级,再启动
t1.start();
t2.setPriority(1);
t2.start();
t3.setPriority(4);
t3.start();
t4.setPriority(8);
t4.start();
t5.setPriority(9);
t5.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
}
}
输出结果:
7、守护线程
线程分为用户线程和守护线程;
虚拟机必须确保用户线程执行完毕,不用等待守护线程执行完毕;
如后台记录日志,监控内存,垃圾回收等…
Thread对象的setDaemon(true)方法可以设置该线程为守护线程。
代码示例:
public class TestDaemon {
public static void main(String[] args) {
God god = new God();
You you = new You();
Thread thread = new Thread(god);
//默认是false表示用户线程,正常的线程都是用户线程
thread.setDaemon(true);
thread.start();
new Thread(you).start();
}
}
class You implements Runnable{
@Override
public void run() {
for (int i = 0; i < 36500; i++) {
System.out.println("好好活着");
}
System.out.println("goodbye!");
}
}
class God implements Runnable{
@Override
public void run() {
while(true) {
System.out.println("上帝保佑着你");
}
}
}
输出结果:
这里有一点值得注意,主线程明明都走完了,而守护线程还在走,也就是他不随主线程走,就类似gc守护线程一样,虚拟机彻底关闭之后他才会关闭。