Java中虽然创建和销毁线程方便,但是特别浪费时间 ,因此出现了线程池。
线程池内部维护了多个线程,当没有任务时就处于等待状态,出现新任务就分配一个空闲线程执行,执行完成继续等待下一次调用。若所有线程处于忙碌,则该任务要么等待,要么创建新的线程。
Java中线程池的接口为:ExecutorService
实现类为:
- FixedThreadPool:线程数固定的线程池;
- CachedThreadPool:线程数根据任务动态调整的线程池;
- SingleThreadExecutor:仅单线程执行的线程池。
import java.util.concurrent.*;
public class Main {
public static void main(String[] args) {
// 创建一个固定大小的线程池:
ExecutorService es = Executors.newFixedThreadPool(4);
for (int i = 0; i < 6; i++) {
es.submit(new Task("" + i));
}
// 关闭线程池:
es.shutdown();
}
}
class Task implements Runnable {
private final String name;
public Task(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println("start task " + name);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
System.out.println("end task " + name);
}
}
以上是线程池从创建,任务调用,到关闭的全过程。
创建新的线程池:Executors.newFixedThreadPool();
任务调用:submit();
线程池关闭:shutdown();
ScheduledThreadPool
放入ScheduledThreadPool线程池的任务可以定期执行。
定期执行分为两种:
第一种从任务执行开始计时,三秒后重新执行。
// 2秒后开始执行定时任务,每3秒执行:
ses.scheduleAtFixedRate(new Task("fixed-rate"), 2, 3, TimeUnit.SECONDS);
第二种从任务结束开始计时,三秒后重新执行。
// 2秒后开始执行定时任务,以3秒为间隔执行:
ses.scheduleWithFixedDelay(new Task("fixed-delay"), 2, 3, TimeUnit.SECONDS);
Future
一般线程创建会实现Runnable接口,而还有一个接口为Callable,它多了一个返回值,
当Callable任务提交给线程池会返回一个类型即Future类型。
// 定义任务:
Callable<String> task = new Task();
// 提交任务并获得Future:
Future<String> future = executor.submit(task);
它定义的方法有:
- get():获取结果(可能会等待)
- get(long timeout, TimeUnit unit):获取结果,但只等待指定的时间;
- cancel(boolean mayInterruptIfRunning):取消当前任务;
- isDone():判断任务是否已完成。
ForkJoin
ForkJoin为另一种线程池,它可以将很大的任务拆分为小任务去执行。
任务类必须继承自RecursiveTask或RecursiveAction。
使用Fork/Join模式可以进行并行计算以提高效率