Java 线程状态与常用操作
Java 线程状态与常用操作
线程驱动任务(任务在线程中执行)。线程可以是以下5种状态:新建、就绪、运行、阻塞或结束。

线程的5种状态
新建状态(New)
新创建一个线程时,它就进入新建状态(New)。
就绪状态(Ready)
调用线程的start()方法启动线程后,它就进入就绪状态(Ready)。
就绪状态是可运行的,但可能还没有开始运行,操作系统必须为它分配CPU时间。
运行状态(Running)
就绪线程开始运行时,它就进入运行状态。
如果给定的CPU时间用完或调用线程的yield()方法,处于运行状态的线程可能进入就绪状态。
阻塞状态(Blocked)
有几种原因可能使线程进入阻塞状态(即非活动状态):
- 线程自己调用了
join()、sleep()或wait()方法。 - 线程在等待I/O操作的完成。
当使得其处于非激活状态的动作不起作用时,阻塞线程可能被重新激活(例如,如果线程处于休眠状态并且休眠时间已过期)。
结束状态(Finished)
如果一个线程执行完它的run()方法,这个线程就被结束(finished)。
线程状态判断与中断方法
isAlived() 方法
用于判断线程状态的方法。
- 如果线程处于就绪、阻塞或者运行状态,返回
true。 - 如果线程处于新建且没有启动,或者已经结束,返回
false。
interrupt() 方法
中断一个线程:
- 当线程处于就绪或者运行状态时,给它设置一个中断标志。
- 当线程处于阻塞状态时,它将被唤醒并进入就绪状态,同时抛出异常
java.lang.InterruptedException。
线程核心方法
yield() 方法
为其它线程临时让出CPU时间。
- 礼让线程,让当前正在执行线程暂停。
- 不是阻塞线程,而是将线程从运行状态转入就绪状态。
- 让CPU调度器重新调度。
- 调用了
yield方法之后,如果没有其他等待执行的线程,当前线程会马上恢复执行。
代码示例
package com.thread;
import java.util.concurrent.Executors;
/**
* @author Jing61
*/
public class PrintNum implements Runnable{
private int count;
public PrintNum(int count) {
this.count = count;
}
@Override
public void run() {
for(int i = 0; i < count; i++) {
System.out.println(" " + i);
/*
*yield()方法为其它线程临时让出cpu时间:
* 让当前正在执行线程暂停,不是阻塞线程,而是将线程转入就绪状态
* 调用了yield方法之后,如果没有其他等待执行的线程,此时当前线程就会马上恢复执行
*/
Thread.yield();//每打印一个数字后,该任务的线程会让出时间给其它线程
}
}
public static void main(String[] args) {
var excutor = Executors.newCachedThreadPool();
excutor.execute(new PrintNum(10));
excutor.execute(new PrintNum(10));
excutor.shutdown();
}
}
sleep() 方法
将该线程设置为休眠以确保其它线程的执行,休眠时间为指定的毫秒数。
sleep(时间)指定当前线程阻塞的毫秒数。sleep存在异常InterruptedException。sleep时间达到后线程进入就绪状态。sleep可以模拟网络延时、倒计时等。- 每一个对象都有一个锁,
sleep不会释放锁。
代码示例
package com.thread;
/**
* @author Jing61
*/
public class Racer implements Runnable {
private String victor;
private boolean over = false;
@Override
public void run() {
for(int steps = 1; steps <= 100; steps++) {
System.out.println(Thread.currentThread().getName() + "跑了:" + steps + "m");
try {
if(steps % 20 == 0 && Thread.currentThread().getName().equals("rabbit")) {
// 使线程停止运行一段时间,将处于阻塞状态;如果调用了sleep方法之后,没有其他等待执行的线程,这个时候当前线程不会马上恢复执行!
// 兔子跑得快
Thread.sleep(3000);
} else if (Thread.currentThread().getName().equals("tortoise")) {
// 乌龟跑得慢
Thread.sleep(50);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
gameOver(steps);
if(over) {
System.out.println("比赛结束");
break;
}
}
}
//比赛是否结束
public void gameOver(int steps) {
if(victor != null) over = true;
if(steps == 100) {
victor = Thread.currentThread().getName();
System.out.println("胜利者===>" + victor);
over = true;
}
over = false;
}
public static void main(String[] args) {
Racer racer = new Racer();
new Thread(racer,"tortoise").start();
new Thread(racer,"rabbit").start();
}
}
join() 方法
使一个线程等待另一个线程的结束(其他线程阻塞)。
代码示例
package com.thread;
/**
* @author Jing61
*/
public class BuyCigarettes {
public static void main(String[] args) {
Thread father = new Father();
father.start();
}
private static class Father extends Thread {
@Override
public void run() {
try {
System.out.println("爸爸想抽烟,发现没有了");
System.out.println("叫来儿子,给100块买包中华");
Thread son = new Son();
son.start();
//老爸想抽烟,需要等待儿子买烟回来(儿子线程执行完毕)
son.join();
System.out.println("爸爸接过烟,对你把头点");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private static class Son extends Thread {
@Override
public void run() {
try {
System.out.println("接过老爸的人民币,屁颠颠出门了");
for (int i = 0; i < 10; i++) {
System.out.println(i + "分钟过去了");
Thread.sleep(1000);
}
System.out.println("手拿一包中华回家了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
线程优先级
Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程。线程调度器按照线程的优先级决定应调度哪个线程来执行。
优先级范围
线程的优先级用数字表示,范围从1到10:
Thread.MIN_PRIORITY = 1Thread.MAX_PRIORITY = 10Thread.NORM_PRIORITY = 5
优先级相关方法
int getPriority():获得线程的优先级。void setPriority(int newPriority):设置线程的优先级。
注意事项
- 优先级的设定建议在
start()调用前。 - 优先级低只是意味着获得调度的概率低,并不是绝对先调用优先级高后调用优先级低的线程。
- 如果总有一个优先级较高的线程在运行,或者有一个相同优先级的线程不退出,那么这个线程可能永远没有运行的机会,这种情况称为资源竞争。
- 为了避免资源竞争,高优先级的线程必须定时的调用
sleep或yield方法,来给低优先级或者相同优先级线程的运行机会。
守护线程(setDaemon())
核心特性
- 线程分为用户线程和守护线程。
- 虚拟机必须确保用户线程执行完毕。
- 虚拟机不用等待守护线程执行完毕。
- 常见用途:后台记录操作日志、监控内存使用等。
代码示例
package com.thread;
/**
* 守护线程
* @author Jing61
*/
public class DaemonTest {
public static void main(String[] args) {
God god = new God();
You you = new You();
Thread t = new Thread(god);
t.setDaemon(true); // 将用户线程调整为守护,用户线程结束,守护线程结束
t.start();
new Thread(you).start();
}
}
class You implements Runnable{
@Override
public void run() {
for(int i = 1;i <= 365 * 200; i++) {
System.out.println("happy life...");
}
System.out.println("GG");
}
}
class God implements Runnable{
@Override
public void run() {
// 死循环,因为守护线程会随着用户线程一起结束
for(;;) {
System.out.println("God bless you...");
}
}
}
线程停止
停止原则
- 不使用JDK提供的
stop()/destroy()、suspend()/resume()方法(它们本身也被JDK废弃了)。 - 提供一个
boolean型的终止变量,当这个变量置为false,则终止线程的运行;或者给Thread变量赋值null表明它已经停止。
代码示例
package com.thread;
/**
* 线程终止
*
* @author Jing61
*/
public class TerminateThread implements Runnable {
//1、加入标识 标记线程体是否可以运行
private boolean flag = true;
private String name;
public TerminateThread(String name) {
this.name = name;
}
@Override
public void run() {
int i = 0;
//2、关联标识,true-->运行 false -->停止
while (flag) {
System.out.println(name + "-->" + i++);
}
}
//3、对外提供方法改变标识
public void terminate() {
this.flag = false;
}
public static void main(String[] args) {
TerminateThread tt = new TerminateThread("PEPPA");
Thread t = new Thread(tt);
t.setPriority(Thread.MAX_PRIORITY);
t.start();
for (int i = 0; i < 100; i++) {
if (i == 80) {
tt.terminate();//线程的终止
System.out.println("tt game over");
}
System.out.println("main-->" + i);
}
}
}
Thread类中的常用方法
推荐使用方法
start():启动一个线程,让线程去执行run()。run():线程要执行的具体内容。currentThread():返回对当前正在执行的线程对象的引用,此方法是静态的,可以直接调用。getName():返回线程的名字。setName():设置线程的名字。isAlive():判断当前线程是否“活着”。sleep(int millious):让当前线程睡眠指定的毫秒数。join():当前线程执行时指定加入另一条线程,直到该加入的线程执行完后,当前线程继续执行。join(int millious):当前线程执行时指定加入另一条线程,无论加入的线程是否执行完,只要指定时间一到,当前线程进入“抢占CPU”状态。yield():暂停当前正在执行的线程对象,并执行其他线程。setPriority():设置线程的优先级。getPriority():获得线程的优先级。
注意:设置线程优先级,优先级高的线程并不意味着一定优先获得CPU控制权,而是获得CPU控制权的概率更高。
wait():让当前线程等待。notify():唤醒正在等待的线程。notifyAll():唤醒正在等待的所有线程。
注意:不推荐使用13 ~ 15的方法,使用锁条件
Lock + Condition替代上述不推荐使用的方法,以获得更灵活、更安全的线程同步控制。

浙公网安备 33010602011771号