通过一个实例来接触一下线程。一个进程正在运行时至少会有一个线程在运行,Java中这种情况也是存在的,这些线程默默地在后台运行,例如,调用 public static void main()方法的线程就是这样的,它由JVM创建。
创建Test.java类,代码如下:
/**
* @author gongguowei01@gmail.com
* @since 2020-01-20
*/
public class Test {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName());
}
}
程序运行结果如图 1-7 所示。在控制台输出的main其实就是一个名称为main的线程在执行main()方法中的代码。另外需要注意的是,在控制台输出的main和main方法没有任何关系,它们仅仅是名字相同而已。
Java的JDK开发包自带了对多线程技术的支持,通过它可以很方便地进行多线程的编程,实现多线程编程主要有两种方式:
- 继承 Thread 类
- 实现 Runnable 接口
在Thread类中,其实也实现了Runnable接口,它们之间具有多态的关系。其实使用继承Thread类的方式创建新线程时,最大的局限是不支持多继承,因为Java语言的特点就是单根继承,所以为了支持多线程,完全可以实现Runnable接口,但这两种方式创建线程的功能是一样的,本质上没有区别。
本篇主要介绍第一种方式,通过继承Thread类创建线程。创建一个自定义的线程类MyThread.java,此类继承自Thread,并且重写run()方法。注意,自定义线程,必须重写Run()来执行实现的代码。代码如下:
/**
* @author gongguowei01@gmail.com
* @since 2020-01-20
*/
public class MyThread extends Thread {
@Override
public void run() {
super.run();
System.out.println("MyThread");
}
}
运行类代码如下:
/**
* @author gongguowei01@gmail.com
* @since 2020-01-20
*/
public class Run {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
System.out.println("运行结束");
}
}
上面的代码使用start()方法来启动一个线程,线程启动后自动调用线程里的run()方法,run()方法里面的代码就是线程对象要执行的任务,是线程执行任务的入口。
运行结果如图 1-8 所示。
从运行结果来看,MyThread.java类中的run()方法执行相对于输出"运行结束"的执行时间晚,因为start()方法的执行比较耗时,也增加了先输出“运行结束”字符串的概率。start()方法耗时的原因是执行了多个步骤,步骤如下。
- 通过JVM通知系统创建Thread。
- 操作系统开辟内存空间并使用Windows SDK中的createThread()函数创建Thread线程对象。
- 操作系统对Thread对象进行调度,以确定执行时机。
- Thread在操作系统中被成功执行。
以上4步完整地执行后耗时肯定大于输出一个“运行结束”字符串的时间。另外,main线程执行start()方法时,不必等待4步都执行完成,它会继续执行start()方法后面的代码。
但在这里,还有非常非常小的机会可能会输出如下运行结果:
MyThread
运行结束
这上面的结果说明执行完整的start()方法的4步后,才执行输出“运行结束”字符串的代码,这也说明线程的执行具有随机性。想要输出这种结果,可以人为地制造它,即在执行输出“运行结束”代码之前先执行代码Thread.sleep(300),让run()方法有充足的时间来先输出“MyThread”,后输出“运行结束”,实例代码如下:
/**
* @author gongguowei01@gmail.com
* @since 2020-01-20
*/
public class Run2 {
public static void main(String[] args) throws InterruptedException {
MyThread myThread = new MyThread();
myThread.start();
Thread.sleep(300);
System.out.println("运行结束");
}
}
在使用多线程技术时,代码的运行结果与代码的执行顺序或调用顺序是无关的。另外,线程是一个子任务,CPU以不确定的方式,或者说以随机的时间来调用线程中的run()方法,所以先输出“运行结束”还是先输出“MyThread”具有不确定性。
多线程在Java中有两种实现方式,一种是继承类Thread,一种是显示接口Runnable。