《Java多线程编程核心技术(第2版)》 —1.2 使用多线程
1.2 使用多线程
想学习一个技术就要“接近”它,所以本节首先通过一个示例来接触一下线程。
一个进程正在运行时至少会有一个线程在运行,这种情况在Java中也是存在的,这些线程在后台默默地执行,例如,调用public static void main()方法的线程就是这样的,而且它由JVM创建。
创建示例项目callMainMethodMainThread,并创建Test.java类,代码如下:
package test;
public class Test {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName());
}
}
程序运行结果如图1-7所示。
在控制台输出的main其实就是一个名称为main的线程在执行main()方法中的代码。另外,需要说明一下,在控制台输出的main和main方法没有任何关系,它们仅仅是名字相同而已。
1.2.1 继承Thread类
Java的JDK开发包已经自带了对多线程技术的支持,通过它可以方便地进行多线程编程。实现多线程编程主要有两种方式:一种是继承Thread类,另一种是实现Runnable接口。
在学习如何创建新的线程前,先来看看Thread类的声明结构:
public class Thread implements Runnable
从上面的源代码中可以发现,Thread类实现了Runnable接口,它们之间具有多态关系,多态结构的示例代码如下:
Runnable run1 = new Thread();
Runnable run2 = new MyThread();
Thread t1 = new MyThread();
其实使用继承Thread类的方式创建新线程时,最大的局限是不支持多继承,因为Java语言的特点是单根继承,所以为了支持多继承,完全可以实现Runnable接口,即一边实现一边继承,但这两种方式创建线程的功能是一样的,没有本质的区别。
本节主要介绍第一种方式。创建名称为t1的Java项目,创建一个自定义的线程类MyThread.java,此类继承自Thread,并且重写run()方法。在run()方法中添加线程要执行的任务代码如下:
package com.mythread.www;
public class MyThread extends Thread {
@Override
public void run() {
super.run();
System.out.println("MyThread");
}
}
运行类代码如下:
package test;
import com.mythread.www.MyThread;
public class Run {
public static void main(String[] args) {
MyThread mythread = new MyThread();
mythread.start();//耗时大
System.out.println("运行结束!");//耗时小
}
}
上面代码使用start()方法来启动一个线程,线程启动后会自动调用线程对象中的run()方法,run()方法里面的代码就是线程对象要执行的任务,是线程执行任务的入口。
程序运行结果如图1-8所示。
从图1-8的程序运行结果来看,MyThread.java类中的run()方法的执行时间相对于输出“运行结束!”的执行时间晚,因为start()方法的执行比较耗时,这也增加了先输出“运行结束!”字符串的概率。start()方法耗时的原因是执行了多个步骤,步骤如下。
1)通过JVM告诉操作系统创建Thread。
2)操作系统开辟内存并使用Windows SDK中的createThread()函数创建Thread线程对象。
3)操作系统对Thread对象进行调度,以确定执行时机。
4)Thread在操作系统中被成功执行。
以上4步完整地执行后所消耗的时间一定大于输出“运行结束!”字符串的时间。另外,main线程执行start()方法时不必等待4步都执行完毕,而是立即继续执行start()方法后面的代码,这4步会与输出“运行结束!”的代码一同执行,由于输出“运行结束!”耗时比较少,所以在大多数的情况下,先输出“运行结束!”,后输出“MyThread”。
但在这里,还是有非常非常小的、非常渺茫的机会能输出如下运行结果:
MyThread
运行结束!
输出上面的结果说明执行完整的start()方法的4步后,才执行输出“运行结束!”字符串的代码,这也说明线程执行的顺序具有随机性。然而由于输出这种结果的机会很小,使用手动的方式来重复执行“Run as”->“Java Application”难以重现,这时可以人为地制造这种输出结果,即在执行输出“运行结束!”代码之前先执行代码Thread.sleep(300),让run()方法有充足的时间来先输出“MyThread”,后输出“运行结束!”,示例代码如下:
package test;
import com.mythread.www.MyThread;
public class Run2 {
public static void main(String[] args) throws InterruptedException {
MyThread mythread = new MyThread();
mythread.start();
Thread.sleep(200);
System.out.println("运行结束!");
}
}
在使用多线程技术时,代码的运行结果与代码的执行顺序或调用顺序是无关的。另外,线程是一个子任务,CPU以不确定的方式,或者说是以随机的时间来调用线程中的run()方法,所以先输出“运行结束!”和先输出“MyThread”具有不确定性。
如果多次调用start()方法,则出现异常Exception in thread “main” java.lang. Illegal-ThreadStateException。
- 点赞
- 收藏
- 关注作者
评论(0)