Java多线程学习(四)---控制线程

控制线程

摘要java

Java的线程支持提供了一些便捷的工具方法,经过这些便捷的工具方法能够很好地控制线程的执行并发

1. join线程控制,让一个线程等待另外一个线程完成的方法工具

2. 后台线程,又称为守护线程或精灵线程。它的任务是为其余的线程提供服务,若是全部的前台线程都死亡,后台线程会自动死亡spa

3. 线程睡眠sleep,让当前正在执行的线程暂停一段时,并进入阻塞状态线程

4. 线程让步yield,让当前正在执行的线程暂停,但它不会阻塞该线程,它只是将该线程转入就绪状态code

1、join线程

Thread提供了让一个线程等待另外一个线程完成的方法join()方法。当在某个程序执行流中调用其余线程的join()方法时,调用线程将被阻塞,直到被join()方法加入的join线程执行完为止。join()方法一般由使用线程的程序调用,以将大问题划分红许多小问题,每一个小问题分配一个线程。当全部的小问题都获得处理后,再调用主线程来进一步操做。对象

  1. package test;
  2.  
  3. public class JoinThread extends Thread {
  4.    // 提供一个有参数的构造器,用于设置该线程的名字
  5.    public JoinThread(String name) {
  6.       super(name);
  7.    }
  8.  
  9.    // 重写 run方法,定义线程执行体
  10.    public void run() {
  11.       for (int i = 0; i < 100; i++) {
  12.          System.out.println(getName() + "" + i);
  13.       }
  14.    }
  15.  
  16.    public static void main(String[] args) throws Exception {
  17.       // 启动子线程
  18.       new JoinThread(" 新线程 ").start();
  19.       for (int i = 0; i < 100; i++) {
  20.          if (i == 20) {
  21.             JoinThread jt = new JoinThread(" Join的线程 ");
  22.             jt.start();
  23.             // main 线程调用了 jt线程的join()方法,main线程
  24.             // 必须等 jt执行结束才会向下执行
  25.             jt.join();
  26.          }
  27.          System.out.println(Thread.currentThread().getName() + "" + i);
  28.       }
  29.    }
  30. }

运行结果: blog

main 0 资源

main 1 字符串

main 2

main 3

新线程 0

main 4

新线程 1

main 5

新线程 2

main 6

新线程 3

main 7

新线程 4

main 8

新线程 5

main 9

新线程 6

新线程 7

新线程 8

main 10

新线程 9

main 11

新线程 10

main 12

新线程 11

main 13

新线程 12

main 14

新线程 13

main 15

新线程 14

main 16

…………

main 18

新线程 17

main 19

新线程 18

新线程 19

…………

新线程 99

被Join的线程 0

…………

被Join的线程 99

main 20

上面程序中一共有3个线程,主方法开始时就启动了名为"新线程"的子线程,该子线程将会和main线程并发执行。当主线程的循环变量i等于20时启动了名为"被Join的线程"的线程,该线程不会和main线程并发执行。main线程必须等该线程执行结束后才能够向下执行。在名为"被Join的线程"的线程执行时,实际上只有2个子线程并发执行而主线程处于等待状态。运行上面程序。从上面的运行结果能够看出,主线程执行到i=20时启动,并join了名为"被Join的线程"的线程,因此主线程将一直处于阻塞状态,直到名为"被Join的线程"的线程执行完成。

2、后台线程

有一种线程,它是在后台运行的,它的任务是为其余的线程提供服务,这种线程被称为后台线程Daemon Thread),又称为守护线程精灵线程JVM的垃圾回收线程就是典型的后台线程。后台线程有个特征:若是全部的前台线程都死亡,后台线程会自动死亡。

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

  1. public class DaemonThread extends Thread {
  2.    // 定义后台线程的线程执行体与普通线程没有任何区别
  3.    public void run() {
  4.       for (int i = 0; i < 1000; i++) {
  5.          System.out.println(getName() + "" + i);
  6.       }
  7.    }
  8.  
  9.    public static void main(String[] args) {
  10.       DaemonThread t = new DaemonThread();
  11.       // 将此线程设置成后台线程
  12.       t.setDaemon(true);
  13.       // 启动后台线程
  14.       t.start();
  15.       for (int i = 0; i < 10; i++) {
  16.          System.out.println(Thread.currentThread().getName() + "" + i);
  17.       }
  18.       // ----- 程序执行到此处,前台线程( main线程)结束------
  19.       // 后台线程也应该随之结束
  20.    }
  21. }

运行结果:

main 0

Thread-0 0

main 1

Thread-0 1

main 2

Thread-0 2

main 3

Thread-0 3

main 4

Thread-0 4

main 5

Thread-0 5

main 6

Thread-0 6

main 7

Thread-0 7

main 8

Thread-0 8

main 9

Thread-0 9

Thread-0 10

Thread-0 11

Thread-0 12

Thread-0 13

Thread-0 14

Thread-0 15

Thread-0 16

Thread-0 17

Thread-0 18

Thread-0 19

上面程序中的t线程设置成后台线程,而后启动该线程,原本该线程应该执行到i等于999时才会结束,但运行程序时不难发现该后台线程没法运行到999,由于当主线程也就是程序中惟一的前台线程运行结束后,JVM会主动退出,于是后台线程也就被结束了。Thread类还提供了一个isDaemon0方法,用于判断指定线程是否为后台线程

从上面程序能够看出,主线程默认是前台线程, t线程默认也是前台线程。并非全部的线程默认都是前台线程,有些线程默认就是后台线程——前台线程建立的子线程默认是前台线程后台线程建立的子线程默认是后台线程

前台线程死亡后,JVM会通知后台线程死亡,但从它接收指令到作出响应,须要必定时间。并且要将某个线程设置为后台线程,必须在该线程启动以前设置,也就是说setDaemon(true)必须在start()方法以前调用,不然会引起IllegaIThreadStateException异常。

3、线程睡眠---sleep

若是须要让当前正在执行的线程暂停一段时,并进入阻塞状态,则能够经过调用Thread类的静态sleep()方法来实现。当当前线程调用sleep()方法进入阻塞状态后,在其睡眠时间段内,该线程不会得到执行的机会,即便系统中没有其余可执行的线程,处于sleep()中的线程也不会执行,所以sleep()方法经常使用来暂停程序的执行。

下面程序调用sleep()方法来暂停主线程的执行,由于该程序只有一个主线程,当主线程进入睡眠后,系统没有可执行的线程,因此能够看到程序在sleep()方法处暂停

public class SleepTest {

    public static void main(String[] args) throws Exception {

        for (int i = 0; i < 10; i++) {

            System.out.println("当前时间: " + new Date());

            // 调用sleep方法让当前线程暂停1s

            Thread.sleep(1000);

        }

    }

}

上面程序中sleep()方法将当前执行的线程暂停1秒,运行上面程序,看到程序依次输出10条字符串,输出2条字符串之间的时间间隔为1秒。

4、线程让步---yield()

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

实际上,当某个线程调用了yield()方法暂停以后,只有优先级与当前线程相同,或者优先级比当前线程更高的处于就绪状态的线程才会得到执行的机会。下面程序使用yield()方法来让当前正在执行的线程暂停。

  1. public class YieldTest extends Thread {
  2.    public YieldTest(String name) {
  3.       super(name);
  4.    }
  5.  
  6.    // 定义 run方法做为线程执行体
  7.    public void run() {
  8.       for (int i = 0; i < 50; i++) {
  9.          System.out.println(getName() + "" + i);
  10.          // i等于20时,使用yield方法让当前线程让步
  11.          if (i == 20) {
  12.             Thread.yield();
  13.          }
  14.       }
  15.    }
  16.  
  17.    public static void main(String[] args) throws Exception {
  18.       // 启动两条并发线程
  19.       YieldTest yt1 = new YieldTest(" 高级 ");
  20.       // ty1线程设置成最高优先级
  21.       // yt1.setPriority(Thread.MAX_PRIORITY);
  22.       yt1.start();
  23.       YieldTest yt2 = new YieldTest(" 低级 ");
  24.       // yt2线程设置成最低优先级
  25.       // yt2.setPriority(Thread.MIN_PRIORITY);
  26.       yt2.start();
  27.    }
  28. }

运行结果:

高级 0

低级 0

高级 1

低级 1

高级 2

低级 2

高级 3

低级 3

高级 4

低级 4

高级 5

低级 5

高级 6

低级 6

高级 7

低级 7

高级 8

低级 8

高级 9

低级 9

高级 10

低级 10

高级 11

低级 11

高级 12

低级 12

高级 13

低级 13

高级 14

低级 14

高级 15

低级 15

高级 16

低级 16

高级 17

低级 17

高级 18

低级 18

高级 19

低级 19

高级 20

低级 20

高级 21

低级 21

高级 22

低级 22

高级 23

低级 23

高级 24

低级 24

高级 25

……

低级 48

高级 49

低级 49

上面程序中调用的yield()静态方法让当前正在执行的线程暂停,让系统线程调度器从新调度。因为程序中第21行、第25行代码处于注释状态——即两个线程的优先级彻底同样,因此当一个线程使用yield()方法暂停后,另外一个线程就会开始执行。若是将YieldTest.java程序中两行代码的注释取消,也就是为两个线程分别设置不一样的优先级,则程序的运行结果示。

高级 0

高级 1

高级 2

高级 3

高级 4

高级 5

高级 6

高级 7

高级 8

高级 9

高级 10

高级 11

高级 12

高级 13

高级 14

高级 15

高级 16

高级 17

高级 18

高级 19

高级 20

高级 21

高级 22

高级 23

高级 24

高级 25

高级 26

高级 27

高级 28

高级 29

高级 30

高级 31

高级 32

高级 33

高级 34

高级 35

高级 36

高级 37

高级 38

高级 39

高级 40

高级 41

高级 42

高级 43

高级 44

高级 45

高级 46

高级 47

高级 48

高级 49

低级 0

低级 1

………

低级 48

低级 49

关于sleep()方法和yield()方法的区别以下:

1. sleep()方法暂停当前线程后,会给其余线程执行机会,不会理会其余线程的优先级;但yield()方法只会给优先级相同,或优先级更高的线程执行机会

2. sleep()方法会将线程转入阻塞状态,直到通过阻塞时间才会转入就绪状态:而yield()不会将线程转入阻塞状态,它只是强制当前线程进入就绪状态。所以彻底有可能某个线程调用yield()方法暂停以后,当即再次得到处理器资源被执行

3. sleep()方法声明抛出了InterruptcdException异常,因此调用sleep()方法时要么捕捉该异常,要么显式声明抛出该异常;而yield()方法则没有声明抛出任何异常

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

若是,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】。
若是,您但愿更容易地发现个人新博客,不妨点击一下左下角的【关注我】。
若是,您对个人博客所讲述的内容有兴趣,请继续关注个人后续博客,我是【Sunddenly】。

本文版权归做者和博客园共有,欢迎转载,但未经做者赞成必须保留此段声明,且在文章页面明显位置给出原文链接,不然保留追究法律责任的权利

相关文章
相关标签/搜索