前言
进程是一个执行中程序的实例,是操作系统进行资源分配和调度的一个独立单元,比如打开一个浏览器就启动了一个浏览器进程。线程是进程中一个单一的程序控制流,是 CPU 调度和分派的基本单元,比如采用多线程多窗口的浏览器,每个浏览器窗口都是一个线程。进程在执行时拥有独立的内存空间,进程中的线程可以共享进程的内存空间。在 Java 的世界中,进程可以拥有多个并发执行的线程,多线程是实现并发任务的方式。本文主要就 Java 线程的基础知识做一个梳理。
线程创建和启动
1. 实现 java.lang.Runnable 接口
定义线程执行的任务,需要实现 Runnable 接口并编写 run 方法。
public interface Runnable {
/**
* Starts executing the active part of the class' code. This method is
* called when a thread is started that has been created with a class which
* implements {@code Runnable}.
*/
public void run();
}
以下示例通过实现 Runnable 接口来模拟火箭发射之前倒计时的任务:
public class LiftOff implements Runnable {
private int countDown;
public LiftOff(int countDown) {
this.countDown = countDown;
}
private String status() {
return countDown > 0 ? String.valueOf(countDown) : "LiftOff!";
}
@Override
public void run() {
while (countDown-- > 0) {
System.out.println(status());
}
}
}
2. 实现 java.util.concurrent.Callable 接口
Callable 接口的 call 方法可以在任务执行结束后产生一个返回值,以下是 Callable 接口的定义:
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
可以使用 ExecutorService 类的 submit 方法提交实现了 Callable 接口的任务,submit 方法会产生一个 Future 对象,它用 call 方法返回结果的类型进行了参数化。可以通过 isDone 方法来查询 Future 是否已经完成,如果已完成,则可以调用 get 方法获取结果。也可以不使用 isDone 方法进行检测就直接调用 get 方法获取结果,此时如果结果还未准备就绪,get 方法将阻塞直到结果准备就绪。
class TaskWithResult implements Callable<String> {
private int id;
public TaskWithResult(int id) {
this.id = id;
}
@Override
public String call() throws Exception {
return "Result of TaskWithResult " + id;
}
}
public class CallableDemo {
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
// save Future object of submitted task
List<Future<String>> futureList = new CopyOnWriteArrayList<>();
for (int i = 0; i < 10; i++) {
futureList.add(exec.submit(new TaskWithResult(i)));
}
// waiting for all results
while (futureList.size() > 0) {
for (Future<String> future : futureList) {
if (future.isDone()) {
try {
System.out.println(future.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} finally {