6.2 创建和使用线程
创建和使用线程,就是要让这个线程完成一些特定的功能。在 Java 中,提供了java.lang. Thread类来完成多线程的编程,这个类也提供了大量的方法方便操作线程。在编写一个线程类时,可以继承自这个Thread类,完成线程的相关工作。
如果编写的线程类要继承其他类,但Java又不支持多继承,所以Java还提供了另外一种创建线程的方式,即实现Runnable接口。
6.2.1 创建线程类
如果线程类直接继承Thread类,其代码结构大致如下:
class 类名 extends Thread{
//属性
//其他方法
public void run(){
//线程需要执行的核心代码
}
}
从线程类的代码结构可以看出,一个线程的核心代码需要写在run()方法里。也就是说,当线程从就绪状态,通过调度程序分配CPU资源,进入运行状态后,执行的代码即run()方法里面的代码。
如果线程类是实现Runnable接口的,其代码结构大致如下:
class 类名 implements Runnable{
//属性
//其他方法
public void run(){
//线程需要执行的核心代码
}
}
和继承Thread类非常类似,实现Runnable接口的线程类也需要编写run()方法,将线程的核心代码置于该方法中。但是 Runnable 接口并没有任何对线程的支持,因此还必须创建 Thread 类的实例,通过Thread 类的构造函数来创建线程类。
类名 对象名 = new 类名();
Thread 线程对象名 = new Thread(对象名);
6.2.2 多线程使用
下面的例子,分别使用继承Thread类和实现Runnable接口两种方式创建了两个线程类,并通过调用start()方法启动线程。具体程序代码如下:
public class TestThread {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new MyThread1();
MyThread2 mt2 = new MyThread2();
Thread t2 = new Thread(mt2);
t1.start();
t2.start();
}
}
//继承自Thread类创建线程类
class MyThread1 extends Thread {
private int i = 0;
//无参构造方法,调用父类构造方法设置线程名称
public MyThread1(){
super("我的线程1");
}
//通过循环判断,输出10次,每次间隔0.5秒
public void run(){
try{
while(i < 10){
System.out.println(this.getName() + "运行第" + (i+1) + "次");
i++;
//在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
sleep(500);
}
}catch(Exception e){
e.printStackTrace();
}
}
}
//实现Runnable接口创建线程类
class MyThread2 implements Runnable{
String name = "我的线程2";
public void run() {
System.out.println(this.name);
}
}
编译、运行程序,运行结果如图6.3所示。因为程序中的注释已对程序进行了详细的描述,这里不再展开解释。
图6.3 多线程程序
程序中,要想启动一个线程,都是通过调用start()方法来启动的,使线程进入就绪状态,等待调度程序分配CPU资源后进入运行状态,执行run()方法里的内容。作为程序员,是不是可以直接调用run()方法,使这个线程运行起来呢?答案是:可以,但也不可以。所谓可以是指的确能直接调用run()方法执行run()方法里的代码,但这只是串行执行run()方法,并没有启动一个线程,让该线程与其他线程并行执行。
在main()方法里的t2.start();代码后增加一句t2.run();,再次编译、运行程序,会发现“我的线程2”输出2次,其中一次是通过t2.start()方法启动线程,执行run()方法输出的,另外一次是直接调用t2.run()方法输出的。
如果在t2.start();和t2.run();两行代码之间增加一句Thread.sleep(2000);,其含义为在2秒内让当前正在执行的线程休眠,再次编译、运行程序,其结果又如何呢?为什么会出现这样的结果呢?