13.2 线程的创建
在Java语言中,可通过系统提供的编程接口去创建线程。
13.2.1 如何创建线程
创建线程的方法一般有两种。
1.通过Runnable接口的方式创建线程
在Java中,线程是一种对象,但不是所有的对象都可以称为线程,只有实现了Runnable接口的类,才可以称为线程。下面先看看Runnable接口的定义。
public interface Runnable
{
public abstract void run();
}
Runnable接口只有一个抽象方法“run()”,要实现这个接口,只要实现这个抽象方法就可以。只有实现了这个接口的类,才有资格称为线程。创建线程的结构如下:
Thread t=new Thread(runnable对象);
runnable对象是指实现了Runnable接口类的对象。当线程执行时,runnable对象中的“run()”方法会被调用。如果想要运行上面创建的线程,还需要调用一个Thread类的方法。
t.start();
【实例13.1】下面举个有关创建线程的实例。
01 ///创建两个线程,让其交替运行
02 public class threadtest
03 {
04 public static void main(String[]args)
05 {
06 compute c=new compute();
07 compute1 c1=new compute1();
08 Thread t=new Thread(c);
09 Thread t1=new Thread(c1);
10 t.start();
11 t1.start();
12 }
13 }
14 ///这是一个线程类
15 ///这个类主要是用来通过循环语句输出数字
16 class compute implements Runnable
17 {
18 int i=0;
19 public void run()
20 {
21 for(int i=0;i<10;i++)
22 {
23 System.out.println(i);
24 }
25 }
26 }
27 ///这是一个线程类
28 ///这个类主要是用来通过循环语句输出字符串语句
29 class compute1 implements Runnable
30 {
31 public void run()
32 {
33 for(int i=0;i<10;i++)
34 {
35 System.out.println("这个数字是:"+i);
36 }
37 }
38 }
【代码说明】第16~38行创建了两个线程compute和compute1,它们都实现接口Runnable。第6~7行分别创建这两个线程的对象,然后第10~11行开始运行线程。
【运行效果】
0 这个数字是:0
1 这个数字是:1
2 这个数字是:2
3 这个数字是:3
4 这个数字是:4
5 这个数字是:5
6 这个数字是:6
7 这个数字是:7
8 这个数字是:8
9 这个数字是:9
这个程序段中,创建了两个线程,不过读者多次运行后可能结果会有不同。那为什么会输出不同的结果呢?
因为在程序中存在一个执行问题。在Java技术中,线程通常是抢占式的,其不需要时间片的分配。抢占式调度模式指许多线程处于可以运行状态,即等待状态,但实际只有一个线程在运行。该线程一直运行到它终止,或者另一个具有更高优先级变成可运行状态。在后一种情况下,低优先级线程被高优先级线程抢占且获得运行机会。
说明
如果把上面的程序再运行一次,可能结果又不一样了,因为它的抢占方式,在目前这个程序段里是无法控制的。
Java调度器支持不同优先级线程抢占方式,不支持相同优先级线程时间片轮换。但如果所在的系统支持时间片轮换,它也会支持。如何才能输出自己想要的结果?后面会学习如何解决这个问题。
2.通过继承Thread类来创建线程
其实Thread类本身也实现了Runnable接口,所以只要让一个类能够继承Thread类,并覆盖“run()”方法,也就创建了线程。
【实例13.2】下面把前面举过的例子用这种方法重新编写。
01 ///创建一个主运行类
02 ///在主运行方法中,创建两个线程,让其交替运行
03 public class threadtest1
04 {
05 public static void main(String[]args)
06 {
07 compute t=new compute();
08 compute1 t1=new compute1();
09 t.start();
10 t1.start();
11 }
12 }
13 ///创建compute线程类
14 ///在这个线程类中,通过循环语句输出1~10十个整型数据
15 class compute extends Thread
16 {
17 int i=0;
18 public void run()
19 {
20 for(int i=0;i<10;i++)
21 {
22 System.out.println(i);
23 }
24 }
25 }
26 ///创建compute1线程类
27 ///在这个线程类中,通过循环语句输出说明性语句
28 class compute1 extends Thread
29 {
30 public void run()
31 {
32 for(int i=0;i<10;i++)
33 {
34 System.out.println("这个数字是:"+i);
35 }
36 }
37 }
【代码说明】第15~37行创建了两个线程,它们都继承自Thread类。第7~10行创建两个线程对象,并开始运行线程。
【运行效果】
0 这个数字是:0
1 这个数字是:1
2 这个数字是:2
3 这个数字是:3
4 这个数字是:4
5 这个数字是:5
6 这个数字是:6
7 这个数字是:7
8 这个数字是:8
9 这个数字是:9
说明 读者的运行结果可能不同,但没有关系。
创建方式虽然不一样,但运行效果仍然一样。下一节将通过一个稍微复杂的综合实例,让读者对线程的概念有一个更好的认识。通过这个综合实例,也可以复习前面讲过的一些知识,如接口、继承、多态、覆盖等,因为这些知识点在程序开发中一直会出现。