并发是Java语言中一个很重要的特性,使用得当可以显著提高程序的性能。在大二的课程《现代操作系统》中,我已经对线程有一个初步的了解,然而也只是停留在概念层面上。我对知识掌握的薄弱,在实践中展现无遗。废话不多说,现在开始对知识点的总结。
今天写的是Java并发中最基本的线程创建。
首先,正如在此书中的介绍,被线程驱动的称为”任务”。在Java中,以实现Runnable接口的方式,说明此类的对象由线程驱动。实现了Runnable的接口的该任务类必须实现run()方法,此方法是线程驱动时默认的调用方法。
在介绍线程的具体内容时,需要首先讲一下,平时见得多的main方法,实际上也是一个线程。在main方法中可以生成其他线程,但是这些线程之间的关系较为复杂,会产生其中一条线程产生异常却无法传到main方法处的情况,此处先不细表。
定义了任务后,就是到了用线程驱动任务的阶段了。用线程驱动任务有两种方法:
1.使用Thread类
2.使用Executor
首先来看第一种,使用Thread类。这种是Java中传统的线程构建方式。使用方法是将一个Runnable对象传入Thread构造器,然后调用Thread对象的start()方法。
class TestTask implements Runnable{
private int num = 0;
public void run(){
while(num < 5){
System.out.println(num);
num++;
}
}
}
public class Test1{
public static void main(String [] args){
Thread t = new Thread(new TestTask());
t.start();
System.out.println("This is main method");
}
}
这是运行结果:
这里出现一个乍看上去比较奇特的问题:明明线程t是先驱动的,为什么反倒是main方法中的打印语句先出现呢?这是因为两个线程实际上是同时在CPU上运行的(假定为多核处理器,若是非多核处理器等其他情况则按照处理器选择的算法给两个线程安排时间片),而main函数在调用了Thread对象t的start方法后马上返回了,打印语句比线程t驱动任务的速度快,所以先出现了打印语句的结果。
为了证实这一观点,我修改了main方法的线程,使其休眠,观察结果。
import java.util.concurrent.*;
class TestTask implements Runnable{
private int num = 0;
public void run(){
while(num < 5){
System.out.println(num);
num++;
}
}
}
public class Test1{
public static void main(String [] args)throws Exception{
Thread t = new Thread(new TestTask());
t.start();
TimeUnit.MILLISECONDS.sleep(10);
System.out.println("This is main method");
}
}
修改后:
结果证实了我的构想。
第二种方法是使用Executor。这种方法是设计模式的一种应用,在客户和任务中间去提供一个中间层,让这个中间对象进行操作。这是JavaSE5及后面的版本中的优选方法。
Executor有几种类型,分别是:
1.CacheThreadPool:为每个任务都开启一个线程。
2.FixedThreadPool:为任务集开启限额的线程。
3.SingleThreadPool:为制定的任务仅开启一个线程。
三种Executor各有用途,此处不再细表。
import java.util.concurrent.*;
class Task implements Runnable{
private int id;
public Task(int id){
this.id = id;
}
public void run(){
System.out.println("This is Thread#" + id);
}
}
public class Test3{
public static void main(String[] args) {
ExecutorService es = Executors.newCachedThreadPool();
for (int i = 0; i < 5 ; i++ ) {
es.execute(new Task(i));
}
System.out.println("This is main method.");
es.shutdown();
}
}
此处是结果:
在三次的测试中,线程执行先后顺序各不相同。