• Java知多少(58)线程Runnable接口和Thread类详解


    大多数情况,通过实例化一个Thread对象来创建一个线程。Java定义了两种方式:

    • 实现Runnable 接口;
    • 可以继承Thread类。


    下面的两小节依次介绍了每一种方式。

    实现Runnable接口

    创建线程的最简单的方法就是创建一个实现Runnable 接口的类。Runnable抽象了一个执行代码单元。你可以通过实现Runnable接口的方法创建每一个对象的线程。为实现Runnable 接口,一个类仅需实现一个run()的简单方法,该方法声明如下:
        public void run( )
    在run()中可以定义代码来构建新的线程。理解下面内容是至关重要的:run()方法能够像主线程那样调用其他方法,引用其他类,声明变量。仅有的不同是run()在程序中确立另一个并发的线程执行入口。当run()返回时,该线程结束。

    在你已经创建了实现Runnable接口的类以后,你要在类内部实例化一个Thread类的对象。Thread 类定义了好几种构造函数。我们会用到的如下:
        Thread(Runnable threadOb, String threadName)
    该构造函数中,threadOb是一个实现Runnable接口类的实例。这定义了线程执行的起点。新线程的名称由threadName定义。

    建立新的线程后,它并不运行直到调用了它的start()方法,该方法在Thread 类中定义。本质上,start() 执行的是一个对run()的调用。 Start()方法声明如下:

    1 void start( )

    下面的例子是创建一个新的线程并启动它运行:

     1 // Create a second thread.
     2 class NewThread implements Runnable {
     3     Thread t;
     4     NewThread() {
     5         // Create a new, second thread
     6         t = new Thread(this, "Demo Thread");
     7         System.out.println("Child thread: " + t);
     8         t.start(); // Start the thread
     9     }
    10 
    11     // This is the entry point for the second thread.
    12     public void run() {
    13         try {
    14             for(int i = 5; i > 0; i--) {
    15                 System.out.println("Child Thread: " + i);
    16                 Thread.sleep(500);
    17             }
    18         } catch (InterruptedException e) {
    19             System.out.println("Child interrupted.");
    20         }
    21         System.out.println("Exiting child thread.");
    22     }
    23 }
    24 
    25 class ThreadDemo {
    26     public static void main(String args[]) {
    27         new NewThread(); // create a new thread
    28         try {
    29             for(int i = 5; i > 0; i--) {
    30                 System.out.println("Main Thread: " + i);
    31                 Thread.sleep(1000);
    32             }
    33         } catch (InterruptedException e) {
    34            System.out.println("Main thread interrupted.");
    35         }
    36         System.out.println("Main thread exiting.");
    37     }
    38 }

    在NewThread 构造函数中,新的Thread对象由下面的语句创建::

    1     t = new Thread(this, "Demo Thread");

    通过前面的语句this 表明在this对象中你想要新的线程调用run()方法。然后,start() 被调用,以run()方法为开始启动了线程的执行。这使子线程for 循环开始执行。调用start()之后,NewThread 的构造函数返回到main()。当主线程被恢复,它到达for 循环。两个线程继续运行,共享CPU,直到它们的循环结束。该程序的输出如下:

     1 Child thread: Thread[Demo Thread,5,main]
     2 Main Thread: 5
     3 Child Thread: 5
     4 Child Thread: 4
     5 Main Thread: 4
     6 Child Thread: 3
     7 Child Thread: 2
     8 Main Thread: 3
     9 Child Thread: 1
    10 Exiting child thread.
    11 Main Thread: 2
    12 Main Thread: 1
    13 Main thread exiting.

    如前面提到的,在多线程程序中,通常主线程必须是结束运行的最后一个线程。实际上,一些老的JVM,如果主线程先于子线程结束,Java的运行时间系统就可能“挂起”。前述程序保证了主线程最后结束,因为主线程沉睡周期1000毫秒,而子线程仅为500毫秒。这就使子线程在主线程结束之前先结束。简而言之,你将看到等待线程结束的更好途径。

    扩展Thread

    创建线程的另一个途径是创建一个新类来扩展Thread类,然后创建该类的实例。当一个类继承Thread时,它必须重载run()方法,这个run()方法是新线程的入口。它也必须调用start()方法去启动新线程执行。下面用扩展thread类重写前面的程序:

     1 // Create a second thread by extending Thread
     2 class NewThread extends Thread {
     3     NewThread() {
     4         // Create a new, second thread
     5         super("Demo Thread");
     6         System.out.println("Child thread: " + this);
     7         start(); // Start the thread
     8     }
     9 
    10     // This is the entry point for the second thread.
    11     public void run() {
    12         try {
    13             for(int i = 5; i > 0; i--) {
    14                 System.out.println("Child Thread: " + i);
    15                 Thread.sleep(500);
    16             }
    17         } catch (InterruptedException e) {
    18             System.out.println("Child interrupted.");
    19         }
    20         System.out.println("Exiting child thread.");
    21     }
    22 }
    23 
    24 class ExtendThread {
    25     public static void main(String args[]) {
    26         new NewThread(); // create a new thread
    27         try {
    28             for(int i = 5; i > 0; i--) {
    29                 System.out.println("Main Thread: " + i);
    30                 Thread.sleep(1000);
    31            }
    32         } catch (InterruptedException e) {
    33             System.out.println("Main thread interrupted.");
    34         }
    35         System.out.println("Main thread exiting.");
    36     }
    37 }

    该程序生成和前述版本相同的输出。子线程是由实例化NewThread对象生成的,该对象从Thread类派生。注意NewThread 中super()的调用。该方法调用了下列形式的Thread构造函数:

    1     public Thread(String threadName)

    这里,threadName指定线程名称。

    选择合适方法

    到这里,你一定会奇怪为什么Java有两种创建子线程的方法,哪一种更好呢。所有的问题都归于一点。Thread类定义了多种方法可以被派生类重载。对于所有的方法,惟一的必须被重载的是run()方法。这当然是实现Runnable接口所需的同样的方法。很多Java程序员认为类仅在它们被加强或修改时应该被扩展。因此,如果你不重载Thread的其他方法时,最好只实现Runnable 接口。这当然由你决定。然而,在本章的其他部分,我们应用实现runnable接口的类来创建线程。

    系列文章:
  • 相关阅读:
    BFS(从数字A变到数字B每次只能换一个数)
    BFS(数字a通过三种操作到数字B)
    dfs+bfs(三种路径问题)
    国际象棋跳马问题
    拓扑排序
    hadoop-hdfs、mapreduce学习随笔
    hive初探2_数据模型
    hive初探_框架组成、简单使用
    Scala学习笔记
    Scala安装
  • 原文地址:https://www.cnblogs.com/Coda/p/4491767.html
Copyright © 2020-2023  润新知