线程总结(二):控制线程

我们把线程创建出来并让其执行,不能让他像放羊一样随便乱跑吧,我们得控制他们的执行,Java 提供了一系列方法来帮助我们控制线程的执行。

join() - 等待一个线程完成

Thread API 中包含了等待另一个线程完成的方法:join() 方法。当调用 Thread.join() 时,调用线程将阻塞,直到目标线程完成为止。

有一种可能的情况,就是需要等待在多个线程上(比如任意一个线程结束或者所有线程结束才会返回),循环调用每个线程的 join 方法是不可行的,这可能导致很奇怪的同步问题。

简单示例

public class TestThread {
    public static void main(String[] args) {
        Thread.currentThread().setName("主线程");
        Thread thread = new MyThread();
        thread.start();

        try {
            for (int i = 0; i < 5; i++) {

                if (i == 3) {
                    thread.join();
                }

                Thread.sleep(1000);
                System.out.print(Thread.currentThread().getName() + (i + 1));
            }
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

class MyThread extends Thread {

    @Override
    public void run() {
        try {
            this.setName("自定义线程");
            for (int i = 0; i < 5; i++) {
                Thread.sleep(1000);
                System.out.print(this.getName() + (i + 1));
            }
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

执行结果

自定义线程1
主线程1
自定义线程2
主线程2
自定义线程3
主线程3
自定义线程4
自定义线程5
主线程4
主线程5

可以看到,在主线程 for 循环中,i = 3 的时候执行了自定义线程的 join() 方法,所以会优先执行自定义线程内容,待自定义线程执行完成之后,才继续执行主线程中的内容。

join() 方法有如下几种使用方式:

  • join()

    等待该线程终止。

  • join(long millis)

    等待该线程终止的时间最长为 millis 毫秒,如果设为值 0 ,则一直要等下去。

  • join(long millis, int nanos)

    等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。


sleep() - 让线程睡眠

Thread API 包含了一个 sleep() 方法,它将使当前线程进入等待状态,即使系统中没有其他可以执行的线程,该县城也不会执行,直到过了指定时间,当过了指定时间后,线程又将进入就绪状态,等待线程调度器的调度。

简单示例

public class TestThread {
    public static void main(String[] args) {
        Thread thread = new MyThread();
        thread.start();
    }
}

class MyThread extends Thread {

    @Override
    public void run() {
        try {
            this.setName("自定义线程");
            for (int i = 0; i < 5; i++) {
                Thread.sleep((i + 1) * 1000);
                System.out.println(this.getName() + (i + 1));
            }
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

上面的例子休眠时间越来越长。

sleep() 方法有以下几种形式可用:

  • sleep(long millis)

    在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。

  • sleep(long millis, int nanos)

    在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。


yield() - 让线程让步

yield() 方法和 sleep() 方法有些类似,但是不同的是,sleep() 在休眠时间完成以前,是一直处于阻塞状态的,而 yield() 方法则不是,它只会将线程强行转为就绪状态,等到线程调度器的再次调度。所以有可能是它让出了 CPU 的使用权,而调度器又将使用权重新交给了它,它又开始执行了。。。

一般情况下,较高优先级的线程调用 yield() 方法让出执行权,而较低权限的线程并不会执行。。。

因演示效果并不明显,故示例略


wait() - 让线程等待

Java 还提供了 wait() 方法来让线程进入等待状态,配合 notify() 和 notifyAll() 方法使用,但涉及到线程同步问题,在讲解完线程同步会讲解 wait() 方法。

后台线程

有一种线程,它运行在后台,为前台的其他线程提供服务,这种线程被称为“后台线程”,又成为“守护线程”。

当所有前台线程执行完之后,后台线程会自动关闭,因为没的可守护的了嘛,哈哈。

调用线程对象的setDaemon(boolean on)方法并将参数设置为 true 时,该线程将转为后台线程,该方法必须在调用 start() 方法前调用。

isDaemon() 方法可以判断这个显示是不是守护线程。

参考资料

  • 《疯狂 Java 讲义 第三版》
Copyright© 2020-2022 li-xyz 冀ICP备2022001112号-1