张小飞的Java之路——第二十五章——多线程——其他

本文介绍了Java中多线程的常见问题,包括如何停止线程(强调避免使用stop方法,推荐使用标志变量配合中断),线程的顺序执行(利用join方法),守护线程的概念及其与一般线程的区别,线程的优先级以及线程的六种状态。文中通过实例代码详细讲解了每种操作和概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

写在前面:

视频是什么东西,有看文档精彩吗?

视频是什么东西,有看文档速度快吗?

视频是什么东西,有看文档效率高吗?


诸小亮:下面我们介绍一些使用线程时常见的问题?

张小飞:都有哪些问题呢?

1. 停止线程

诸小亮:第一,如何停止一个线程

张小飞:这个我知道,Thread类中有一个 stop 方法,可以停止线程

诸小亮:是有一个 stop 方法,不过这个方法已经过时,不推荐使用

张小飞:那还有其他方式吗?

1. 第一种

诸小亮:其实,停止线程的最好方式是让线程正常结束

张小飞:正常结束?能具体的说一说吗?

诸小亮:其实就是,声明一个变量,设置一个开关,比如:

class Hero extends Thread{
    //1. 定义一个boolean值,控制线程是否结束
    boolean bool;
    public void run(){
        for (int i = 0; i < 10; i++) {
            if(bool){
                //2. 当bool=true时,跳出循环,run方法结束,线程也就停止了
                break;
            }
            System.out.println(Thread.currentThread().getName()+"。。。。。。"+i);
            try {Thread.sleep(500);} catch (InterruptedException e) {}
        }
        System.out.println(Thread.currentThread().getName()+"线程结束。。。。。。");
    }
}
public class ThreadDemo {
    public static void main(String[] args) throws Exception {
        Hero hero = new Hero();
        hero.start();

        Thread.sleep(3000);
        System.out.println("main线程休息2秒后,设置bool=true。。。。。。");
        hero.bool = true;
    }
}

结果:

image.png

张小飞:您这不是停止一个线程吧,这不是线程正常跑完 run 方法了吗?

诸小亮:是的,停止线程的最好方式是让线程正常结束

张小飞:原来是这样

2. interrupt

诸小亮:其实 Thread 中有个 interrupt 方法,可以用来停止线程的运行

张小飞:这个方法是???

诸小亮:interrupt,打断正在运行的线程

  • 注意:interrupt 并不会终止线程,它只是将线程的中断标记设为true
  • 使用 isInterrupted() 方法判断线程的终端标记是否为 true

示例:

class Hero extends Thread{
    public void run(){
        for (int i = 0; i < 100000000; i++) {
            //如果当前线程的中断标记是true,跳出循环,线程结束
            if(Thread.currentThread().isInterrupted()){
                System.out.println(Thread.currentThread().getName()+"被打断,跳出循环。。。。。。");
                break;
            }
            System.out.println(Thread.currentThread().getName()+"。。。。。。"+i);
        }
        System.out.println(Thread.currentThread().getName()+"线程结束。。。。。。");
    }
}
public class ThreadDemo {
    public static void main(String[] args) throws Exception {
        Hero hero = new Hero();
        hero.start();

        Thread.sleep(1000);
        System.out.println("main线程休息1秒后,执行interrupt。。。。。。");
        hero.interrupt();
    }
}

结果:
image.png

诸小亮:如果使用 interrupt 方法打断线程,那么就需要调用 isInterrupted 跟它配合使用

张小飞:那,如果run犯法中不调用 isInterrupted 方法呢?

诸小亮:这样的话,线程会一直正常执行,直到 for 循环结束

  • 记住:interrupt 并不会终止线程,它只是将线程的中断标记设为true

张小飞:明白了

3. interrupt 和 sleep

诸小亮:使用 interrupt 时,也有一些注意点

张小飞:需要注意哪些地方?

诸小亮:如果线程在 sleep、wait 时被 interrupt会抛出 _InterruptedException,_比如:

image.png

结果:

image.png
张小飞:这是为什么呢?

诸小亮:你想想,如果你睡觉的时候有人给你一板砖,你会怎么样?

张小飞:。。。。。。

2. 多个线程顺序执行

张小飞:多线程时,也需要顺序执行?

诸小亮:需要看具体场景吧,某些情况下虽然开启了多个线程,但是还是希望他们能个一个接一个的执行

张小飞:但是线程开启后,每个线程都在执行自己的代码,如何让他们顺序执行呢?

诸小亮:Thread中有一个 join 方法,使用它可以让线程顺序执行,比如:

public static void main(String[] args) throws Exception {
    //1. 多个线程之间顺序执行
    Thread t1 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("线程1执行。。。。。。");
        }
    });
    Thread t2 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("线程2执行。。。。。。");
        }
    });
    Thread t3 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("线程3执行。。。。。。");
        }
    });

    t1.start();
    t1.join();//执行join方法后,main线程等待t1结束后,才继续往下执行
    t2.start();
    t2.join();
    t3.start();
    t3.join();
}

结果:
image.png
张小飞:原来是这样,那我觉得这样就没必要开启多线程了

诸小亮:你说的很对,但是面试时有时会就是会这样问,所以记住这个 join 方法就行了

张小飞:好的,好的


诸小亮:另外,一定要注意:t1.join()这句代码是由 main 线程执行的,所以 main 线程会等待

张小飞:等待什么?

诸小亮:。。。。,当然是等待 t1 结束后,在执行 t2.start()

张小飞:哦哦,明白了

3. 守护线程

张小飞:我听说,线程分为守护线程和一般线程,怎么区分它们?

诸小亮:目前我们创建的线程是前台线程,也叫一般线程,线程中还有一种比较特殊的:守护线程

张小飞:那,怎么创建一个守护线程呢?

诸小亮:很简单,通过 setDaemon 方法可以把一个前台线程设置为守护线程

  • 比如:setDaemon(true);

张小飞:那,守护线程和一般线程有什么区别呢?

诸小亮:守护线程跟一般线程差不多,只是结束的时有些区别

  • 当前台线程结束,守护线程会自动结束

示例:

public static void main(String[] args) throws Exception {
    //1. 创建一个线程
    Thread yase = new Thread(new Runnable() {
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName()+"。。。。。。"+i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                }
            }
            System.out.println("yase线程结束。。。。。。。");
        }
    },"yase");
    //2. 设置yase是守护线程
    yase.setDaemon(true);
    yase.start();

    //3. main是一般线程,休眠3秒后结束
    Thread.sleep(3000);
    System.out.println("main线程休息3秒后,over。。。。。。");
}

结果:

image.png

诸小亮:仔细看上面的输出,发现在 main 线程结束后,yase 这个守护线程也自动结束了

张小飞:原来这就是守护线程

诸小亮:另外,当所有前台线程结束后,守护线程才会结束

张小飞:这是什么意思?

诸小亮:来,看下面的代码

public static void main(String[] args) throws Exception {
    //1. 创建一个线程
    Thread yase = new Thread(new Runnable() {
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName()+"。。。。。。"+i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                }
            }
            System.out.println("yase线程结束。。。。。。。");
        }
    },"yase");
    //2. 设置yase是守护线程
    yase.setDaemon(true);
    yase.start();

    //3. daji是一般线程
    Thread daji = new Thread(new Runnable() {
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                System.out.println(Thread.currentThread().getName()+"-----------------------"+i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                }
            }
            System.out.println("daji线程结束。。。。。。。");
        }
    },"daji");
    daji.start();


    //4. main是一般线程,休眠3秒后结束
    Thread.sleep(3000);
    System.out.println("main线程休息3秒后,over。。。。。。");
}

结果:

image.png

诸小亮:看到了吧,main线程执行最后一句代码后,yase 和 daji 线程依旧在运行

张小飞:嗯嗯,看到了,不过 yase 这个守护线程什么时候结束呢?

诸小亮:当 daji 线程结束后,程序中就没有一般线程在运行了,所以 yase 这个守护线程也随之结束

image.png

张小飞:明白了

4. 线程优先级(了解)

张小飞:我听说,线程是分优先级的,优先级高的获取CPU执行权的概率就大,这是真的吗?

诸小亮:没错,线程确实是分优先级的。通过 getPriority、setPriority 方法可以获取和设置线程的优先级,最大值是 10,最小值是 1。

张小飞:如果不设置呢,它默认的优先级是多少?

诸小亮:如果没有设置,默认优先级是 5。

张小飞:给一段代码演示一下,可以吗?

诸小亮:当然可以了,下面代码中,yase 线程的优先级设置成了 10,而 daji 线程的优先级却被设置成了 1

class Hero extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 1000000; i++) {
            System.out.println(Thread.currentThread().getName()+"。。。。。。"+i);
        }
    }
}
public class ThreadDemo {
    public static void main(String[] args) throws Exception {
        Thread yase = new Thread(new Hero(),"yase");
        yase.setPriority(10);//设置优先级,10最大,1最小
        yase.start();

        Thread daji = new Thread(new Hero(), "daji");
        daji.setPriority(1);//设置优先级,10最大,1最小
        daji.start();

        //主线程多睡一会儿
        Thread.sleep(60000);
    }
}

结果:
image.png

诸小亮:注意观察结果,yase线程执行到了54万,daji线程才执行到39万,明显 yase 线程有更高的CPU执行权

5. 线程状态

张小飞:您之前说,线程也是有很多种状态的,那么具体有哪些状态呢?

诸小亮:Thread类中有 State 枚举类,列举了线程状态,一共有 6 种

1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法
2. 运行(RUNNABLE)Java线程中将就绪(ready)和运行中(running)两种状态统称为“运行”
		就绪:线程调用了start()方法后,这时线程位于可运行线程池中,等待获取CPU的使用权
		运行:就绪状态的线程在获得CPU使用权后,变为运行中状态(running)
3. 阻塞(BLOCKED):表示线程等待获取锁
		遇到 synchronized 变为阻塞状态
4. 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(唤醒或打断)
		遇到 wait、join 变为等待状态
5. 超时等待(TIMED_WAITING):跟WAITING不同,可在指定时间后自己运行
		遇到 wait(毫秒数) 变为超时等待
		如果在等待时被唤醒,立即执行
		等待超时后,先尝试获取锁,不能获取则阻塞,获取到就运行
6. 终止(TERMINATED):表示该线程已经执行完毕

诸小亮:下面这张图可以看出它们各个状态之间的转换

image.png

诸小亮:另外,下面表格中是,状态切换时常用方法

start()启动一个线程
run()线程需要执行的代码,run方法结束,该线程结束
sleep(long millis)线程休眠,但不释放锁
join()等待该线程结束,可以让线程顺序执行
wait()/notify()/notifyAll()wait()使当前线程等待,前提:必须先获得锁,一般配合synchronized 关键字使用

只有当 notify/notifyAll() 被执行时候,才会唤醒该线程继续执行

notify/notifyAll() 的执行只是唤醒等待的线程,而不会立即释放锁,锁的释放要看代码块的具体执行情况。所以尽量在使用了notify/notifyAll() 后立即退出临界区,以唤醒其他线程让其获得锁
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值