Java:控制线程

来源:互联网 发布:实木床 品牌 知乎 编辑:程序博客网 时间:2024/05/20 07:33

Thread提供一些便捷的工具方法,通过这些便捷的工具方法可以很好地控制线程的执行。

一、join线程

Thread提供了让一个线程等待另一个线程完成的方法——join()方法。当在某个程序执行流中调用其他线程的join()方法时,调用线程将被阻塞,知道被join()方法加入join线程执行完为止。
join()方法通常由使用线程的程序调用,以将问题划分为许多小问题,每个小问题分配一个线程。当所有的小问题都得到处理之后,再调用主线程来进一步操作。

public class JoinThread extends Thread{    //提供一个有参数的构造器,用于设置该线程的名字    public JoinThread(String name)    {        super(name);    }    //重写run()方法,定义线程执行体    public void run()    {        for(int i = 0 ;i < 100;i++)        {            System.out.println(getName() + " " + i);        }    }    public static void main(String[] args) throws InterruptedException {        //启动子线程        new JoinThread("新线程").start();        for(int i = 0 ;i < 100;i++)        {            if(i == 20)            {                JoinThread jt = new JoinThread("被Join的线程");                jt.start();                //main线程调用了jt线程的join()方法                //main线程必须等jt线程执行结束才会向下执行                jt.join();            }            System.out.println(Thread.currentThread().getName() + " " + i);        }    }}

主程序一共有3个线程,主方法开始时就启动了名为“新线程”的子线程,该子线程将会和main线程并发执行。当主线程的循环变量等i等与20时,启动了名为“被join线程”的线程,该线程不会和main线程并发执行,main线程必须等该线程执行结束后才可以向下执行。

二、后台线程

有一种线程,它是在后台运行的,它的任务是为其他线程提供服务,这种线程被称为“后台线程(Demon Thread)”,又称为“守护线程”或“精灵线程”。JVM的垃圾回收线程就是典型的后台线程。

后台线程有个特征:如果所有的前台线程都死亡,后台线程会自动死亡

调用Thread对象的setDaemon(true)方法可将指定线程设置成后台线程。下面程序将执行线程设置成后台线程,可以看到当所有前台线程死亡时,后台线程随之死亡。当整个虚拟机中只剩下后台线程时,程序就没有运行的必要了,所以虚拟机就退出了。

public class DaemonThread extends Thread{    public void run()    {        for(int i = 0;i < 100;i++)        {            System.out.println(getName() + " " + i );        }    }    public static void main(String[] args) {        DaemonThread dt = new DaemonThread();        //将此线程设置成后台线程        dt.setDaemon(true);        //启动后台线程        dt.start();        for(int i = 0;i < 10;i++)        {            System.out.println(Thread.currentThread().getName()+ " " + i);        }        //程序执行到此数,前台线程(main线程)结束        //后台线程也应该随之结束    }}

从上面程序可以看出,主线程默认是前台线程,t线程默认也是前台线程。并不是所有的线程默认都是前台线程,有些线程默认就是后台线程——前台线程创建的子线程默认是前台线程,后台线程创建的子线程默认就是后台线程。

注意:前台线程死亡后,JVM会通知后台线程死亡,但从它接收指令到做出相应,需要一定时间,而且要将某个线程设置为后台线程,必须在该线程启动之前设置,也就是说,setDaemon(true)必须在start()方法之前调用,否则会引发IlleaglThreadStateException异常。

三、线程睡眠:sleep

如果需要让当前正在执行的线程暂停一段时间,并进入阻塞状态,则可以通过调用sleep()方法来实现。sleep()方法有两张重载形式。

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

当前调用sleep()方法进入阻塞状态后,在其睡眠时间段内,该线程不会获得执行的机会,即使系统中没有其他可执行的线程,处于sleep()中的线程也不会执行,因此sleep()方法常用来暂停程序的执行

四、线程让步:yield

yield()方法时一个和sleep()方法有点相似的方法,它也是Thread类提供的一个静态方法,它也可以让当前正在执行的线程暂停,但它不会阻塞该进程,它只是将该线程转入就绪状态
yield()只是让当前线程暂停一下,让系统的线程调度器重新调度一次完全可能的情况是:当某个线程调用了yield()方法暂停之后,线程调度器又将其调度出来重新执行

实际上,当某个线程调用了yield()方法暂停之后,只有优先级与当前线程相同,或者优先级逼当前线程更高的处于就绪状态的线程才会获得执行的机会。

关于sleep()方法和yield()方法的区别如下。
sleep()方法暂停当前线程后,会给其他线程执行机会,不会理会其他线程的优先级;但yield()方法只会给优先级相同,或优先级更高的线程执行机会

sleep()方法会将线程转入阻塞状态,直到经过阻塞时间才会转入就绪状态而yield()不会将线程转入到阻塞状态,它只是强制当前线程进入就绪状态。因此完全有可能某个线程调用yield()方法暂停之后,立即再次获得处理器资源被执行

sleep()方法声明抛出了InterruptedException异常,所以调用sleep()方法时要么捕捉该异常,要么显式声明抛出该异常。而yield()方法则没有声明抛出任何异常

sleep()方法比yield()方法有更好的可移植性,通常不建议使用yield()方法来控制并发线程的执行

0 0
原创粉丝点击