Java线程控制

本文内容主要来自《疯狂Java讲义》

线程的生命周期

我们先来看下生命周期的图,该图来自《疯狂 Java 讲义》
nhbYf1.png

  • 新建:当一个线程被 new 出来后,他就处于新建状态
  • 就绪:当一个线程调用 start()方法后,他就处于就绪状态
  • 运行:当一个线程获得 CPU,就开始执行线程执行体即 run()方法,此时就进入了运行状态。
  • 阻塞:当运行的线程遇到途中所示的条件时,就进入阻塞状态,此时线程无法继续运行,只能等待;等待时间结束或者达到某种条件后,就可以进入就绪状态,等待 CPU 继续执行剩下的任务
  • 结束:正常执行完 run()或 start(),或者抛出了异常,或者线程中调用 stop(),都会使得线程直接结束

注意:

  • 如果一个线程开始后,无论该线程是否已经结束,无法再对它进行调用 start(),否则会报 IllegalThreadStateException 异常
  • 开启线程是调用 start(),如果直接调用 run()方法,这样无法开启一个线程,只会当作是执行一个实例中的一个方法,不是线程执行体,无法并发执行

线程控制

suspend()、stop()方法因为不安全,已被弃用,suspend()对应的 resume()也弃用了,详细原因可以查看:官方文档

1. 线程加入join

在自己的线程中加入别的线程,也就是调用别的线程的 join()方法,使得自己的线程阻塞,会等待加入的线程结束后才继续执行。

public class JoinThread extends Thread {

    public JoinThread(String name){
        super(name);
    }
    public void run(){
        for (int i = 0; i < 10; i++) {
            System.out.println(getName() + " " + i);
        }
    }
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            if(i == 5){
                JoinThread jt = new JoinThread("被join的线程");
                jt.start();
                jt.join();
            }
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }

}

输出结果:

main 0
main 1
main 2
main 3
main 4
被join的线程 0
被join的线程 1
被join的线程 2
被join的线程 3
被join的线程 4
被join的线程 5
被join的线程 6
被join的线程 7
被join的线程 8
被join的线程 9
main 5
main 6
main 7
main 8
main 9
  • join():等待被 join 的线程执行完成
  • join(long millis):等待被 join 的线程的时间最长为 mills 毫秒。如果在 millis 毫秒内被 join 的线程还没有执行结束,就不再等待。
  • join(long millis, int nanos):等待被 join 的线程的时间最长为 mills 毫秒加 nanos 毫微秒。

2. 后台线程setDaemon

setDaemon() 后台线程是一种在后台运行的,任务是为其他的线程提供服务的线程。JVM 的垃圾回收线程就是一种后台线程。
如果前台线程都结束,后台线程会自动结束。

public class DaemonThread extends Thread {
    public void run(){
        for (int i = 0; i < 10; i++) {
            try {
                sleep(500);
                System.out.println(getName() + " " + i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        DaemonThread dt = new DaemonThread();
        dt.setDaemon(true);   //设置为后台进程
        dt.start();
        for (int i = 0; i < 10; i++) {
            sleep(200);
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }
}

输出结果:

main 0
main 1
Thread-0 0
main 2
main 3
Thread-0 1
main 4
main 5
main 6
Thread-0 2
main 7
main 8
Thread-0 3
main 9
  • 前台线程创建的子线程默认是前台线程,后台线程创建的子线程默认是后台线程
  • 设置后台线程 setDaemon()必须要在 start()方法前调用,否则报错
  • setDaemon():用于设置线程为后台线程。
  • isDaemon():用于判断指定线程是否是后台线程。

3. 线程睡眠sleep

sleep()使当前线程暂停一段时间,进入阻塞状态,时间结束后直接进入就绪态。

public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            if(i == 5){
                sleep(1000);
            }
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }

输出结果中,可以看到当输出到 4 时,会暂停 1s 后才继续输出。

  • static void sleep(long millis):让当前正在执行的线程暂停 millis 毫秒,并进入阻塞状态,该方法受到系统计时器和线程调度器的精度与准确度的影响
  • static void sleep(long millis, int nanos):让当前正在执行的线程暂停 millis 毫秒加 nanos 毫微秒,并进入阻塞状态,该方法受到系统计时器和线程调度器的精度与准确度的影响

4. 线程让步yield

yield()与 sleep()方法类似,会让当前线程暂停,但不会进入阻塞状态,而是直接进入就绪状态,重新等待系统调度。有可能调用 yield()方法后,线程调度器又将其调度出来执行。

public class YieldThread extends Thread{
   public YieldThread(String name){
       super(name);
   }
   public void run(){
       for (int i = 0; i < 10; i++) {
           System.out.println(getName() + " " + i);
           if(i == 5){
               Thread.yield();
           }
       }
   }
   public static void main(String[] args) {

       YieldThread yieldThread = new YieldThread("先");
       yieldThread.start();

       YieldThread yieldThread1 = new YieldThread("后");
      yieldThread1.start();
   }
}

输出结果:

先 0
先 1
先 2
先 3
后 0
后 1
后 2
后 3
后 4
后 5
先 4
先 5
后 6
后 7
后 8
后 9
先 6
先 7
先 8
先 9

我们可以看到,执行顺序是随机的,但是当执行到 5 时,线程都会切换,就是因为 yield() 进行了暂停,使别的线程获得调度机会。

5. 线程优先级

每一个线程都有一定的优先级,其中有三个常量:

  • MAX_PRIORITY:值为 10
  • MIN_PRIORITY:值为 1
  • NORM_PRIORITY:值为 5

也可以自己设置不同的优先级,范围是 1~10 之间的整数。

子线程的优先级都与创建的它的父线程的优先级相同,其中 main 方法默认是普通优先级为 5。

可通过 setPriority()和 getPriority()方法设置或获取当前线程的优先级。

public class PriorityThread extends Thread{
    public PriorityThread(String name){
        super(name);
    }
    public void run(){
        for (int i = 0; i < 10; i++) {
            System.out.println(getName() + " " + i);
            if(i == 5){
                Thread.yield();
            }
        }
    }
    public static void main(String[] args) {

        Thread.currentThread().setPriority(6);
        for (int i = 0; i < 20; i++) {
            if(i == 5){
                PriorityThread low = new PriorityThread("低级");
                low.start();
                System.out.println("低级创建之初的优先级:" + low.getPriority());
                low.setPriority(Thread.MIN_PRIORITY);
            }
            if(i == 10){
                PriorityThread high = new PriorityThread("高级");
                high.start();
                System.out.println("高级创建之初的优先级:" + high.getPriority());
                high.setPriority(Thread.MAX_PRIORITY);
            }
        }
    }
}

输出结果

低级创建之初的优先级:6
高级创建之初的优先级:6
高级 0
高级 1
高级 2
高级 3
高级 4
高级 5
高级 6
高级 7
高级 8
高级 9
低级 0
低级 1
低级 2
低级 3
低级 4
低级 5
低级 6
低级 7
低级 8
低级 9

我们可以看到,虽然低级线程创建的更早,但会被后来的更高级的线程抢占 CPU 执行。而且即使在高级线程中调用了 yield() 暂停回到了就绪态,调度器也还是调度了高优先级的线程先执行。

8. 线程中断

  • interrupt():设置该线程的中断信号为 true
  • isInterrupted():返回该线程的中断信号
  • interrupted():返回该线程的中断信号,并将中断信号复位为 false

当一个线程调用 interrupt() 时,如果他是运行状态,那么没有任何影响,只会将该线程的中断信号设为 true;如果他是阻塞状态,因为阻塞状态下没有 CPU 给他设置中断信号,就会抛出异常 InterruptedException,被 catch 捕获,使得该线程提前终止阻塞状态,执行 catch 里的内容。

由此,我们就可以优雅的停止线程,我们自己控制停止的过程。

如果想让一个正在运行的线程停止,可以对中断信号进行判断,接收到中断信号后执行终止操作;

如果想让一个阻塞的线程停止,可以让他抛出异常提前终止阻塞状态,进入 catch 中执行终止操作。

示例如下:

public class InterruptThread extends Thread {

    public void run() {

        while (!isInterrupted()) {       //判断中断信号
            System.out.println("线程运行中...");
        }
        System.out.println("线程退出循环,停止...");
    }
    public static void main(String args[]) throws Exception {
        InterruptThread thread = new InterruptThread();
        System.out.println("线程开始...");
        thread.start();
        Thread.sleep(1);                     //让main睡眠
        System.out.println("要求线程停止...");
        thread.interrupt();
        System.out.println("main函数结束...");
    }
}

输出结果:

线程开始...
线程运行中...
线程运行中...
线程运行中...
线程运行中...
线程运行中...
线程运行中...
线程运行中...
线程运行中...
要求线程停止...
main函数结束...
线程退出循环,停止...
public class InterruptThread extends Thread {

    public void run() {
        System.out.println("线程运行中...");
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            // 接收到一个中断异常(InterruptedException),从而提早地终结被阻塞状态
            System.out.println("阻塞状态线程被中断...");
        }
        System.out.println("线程退出循环,停止...");
    }
    public static void main(String args[]) throws Exception {
        InterruptThread thread = new InterruptThread();
        System.out.println("线程开始...");
        thread.start();
        Thread.sleep(1);                     //让main睡眠
        System.out.println("要求线程停止...");
        thread.interrupt();
        System.out.println("main函数结束...");
    }
}

输出结果:

线程开始...
线程运行中...
要求线程停止...
main函数结束...
阻塞状态线程被中断...
线程退出循环,停止...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值