Java 线程状态与常用操作

Java 线程状态与常用操作

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

线程的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 = 1
  • Thread.MAX_PRIORITY = 10
  • Thread.NORM_PRIORITY = 5

优先级相关方法

  • int getPriority():获得线程的优先级。
  • void setPriority(int newPriority):设置线程的优先级。

注意事项

  • 优先级的设定建议在start()调用前。
  • 优先级低只是意味着获得调度的概率低,并不是绝对先调用优先级高后调用优先级低的线程。
  • 如果总有一个优先级较高的线程在运行,或者有一个相同优先级的线程不退出,那么这个线程可能永远没有运行的机会,这种情况称为资源竞争。
  • 为了避免资源竞争,高优先级的线程必须定时的调用sleepyield方法,来给低优先级或者相同优先级线程的运行机会。

守护线程(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类中的常用方法

推荐使用方法

  1. start():启动一个线程,让线程去执行run()
  2. run():线程要执行的具体内容。
  3. currentThread():返回对当前正在执行的线程对象的引用,此方法是静态的,可以直接调用。
  4. getName():返回线程的名字。
  5. setName():设置线程的名字。
  6. isAlive():判断当前线程是否“活着”。
  7. sleep(int millious):让当前线程睡眠指定的毫秒数。
  8. join():当前线程执行时指定加入另一条线程,直到该加入的线程执行完后,当前线程继续执行。
  9. join(int millious):当前线程执行时指定加入另一条线程,无论加入的线程是否执行完,只要指定时间一到,当前线程进入“抢占CPU”状态。
  10. yield():暂停当前正在执行的线程对象,并执行其他线程。
  11. setPriority():设置线程的优先级。
  12. getPriority():获得线程的优先级。

注意:设置线程优先级,优先级高的线程并不意味着一定优先获得CPU控制权,而是获得CPU控制权的概率更高。

  1. wait():让当前线程等待。
  2. notify():唤醒正在等待的线程。
  3. notifyAll():唤醒正在等待的所有线程。

注意:不推荐使用13 ~ 15的方法,使用锁条件 Lock + Condition 替代上述不推荐使用的方法,以获得更灵活、更安全的线程同步控制。

posted @ 2025-11-14 16:14  Jing61  阅读(13)  评论(0)    收藏  举报