多线程概念和相关包:
线程池:提供一个线程队列,队列中保存着所有等待状态的线程。避免了创建与销毁的额外开销,提高了响应的速度。
线程池的体系结构:
java.util.concurrent.Executor 负责线程的使用和调度的根接口
|--ExecutorService 子接口: 线程池的主要接口
|--ThreadPoolExecutor 线程池的实现类
|--ScheduledExceutorService 子接口: 负责线程的调度
|--ScheduledThreadPoolExecutor : 继承ThreadPoolExecutor,实现了ScheduledExecutorService
工具类:Executors
ExecutorService newFixedThreadPool() : 创建固定大小的线程池
ExecutorService newCachedThreadPool() : 缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量。
ExecutorService newSingleThreadExecutor() : 创建单个线程池。 线程池中只有一个线程ScheduledExecutorService newScheduledThreadPool() : 创建固定大小的线程,可以延迟或定时的执行任务
future:
在并发方面引入了Future这个概念。把所有不在主线程执行的代码都附加了将来这个灵魂。主线程只负责其它并发线程的创建、启动、监视和处理并发线程完成任务或发生异常时的回调。其它情况,则交给并发线程自己去处理。而双方之间的沟通,就是通过一个个被称之为 「 将来 」 的类出处理。
Future 定义在 java.util.concurrent 包中,这是一个接口,自 Java 1.5 以来一直存在的接口,用于处理异步调用和处理并发编程
例子:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Main {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//1 创建线程池(执行器池)
ExecutorService Executorspool = Executors.newFixedThreadPool(5);
List<Future<Integer>> list=new ArrayList<Future<Integer>>();
for (int i = 0; i < 10; i++) {
// ---- 程序A ----
Future<Integer> future=Executorspool.submit(new Callable<Integer>() {
public Integer call() throws Exception {
int sum=0;
for (int i = 0; i <=10; i++) {
//Thread.sleep(1000); 定义了5个执行器,这里开起了话将会看到效果
sum+=i;
}
return sum;
}
});
// ---- 程序A ----
list.add(future);
}
// 主线程,跟其他10个线程互不干扰
System.out.println("Main Thread!!!");
Executorspool.shutdown();
for (Future<Integer> future : list) {
// 打印结果数据
System.out.println("Hello: "+future.get());
}
}
}
程序主要逻辑说明
程序A部分实际执行的计算是作为 Lambda 表达式参数传递给 call() 方法。这个实际执行的代码; Lambda 其实就是一个 Callable 实例,Callable 是一个接口,用于表示一个任务,这个任务可以返回值。Callable 接口只有一个方法 call();
Callable 实例创建完成后并不会立即执行,我们仍然需要将它传递给一个执行器( Executor , 执行程序 ) ,这个执行器将负责在新线程中启动该任务并返回一个包含了值的 Future 对象;这个执行器,是 Executor 的实例,通常,它是一个 ExecutorService 类的实例ExecutorService Executorspool = Executors.newFixedThreadPool(5);
上面我们创建了一个能够处理5个线程的 ExecutorService,有了 ExecutorService 对象,调用它的 submit() 并传递 Callable 作为参数即可。 submit() 会启动任务并返回一个 FutureTask 对象。
FutureTask是一个类,实现了 Future 接口,在 java.util.concurrent 包中定义
Executorspool.shutdown();
此部分是关闭了提交通道submit() ,停止接收新任务,原来的任务继续执行;区别于shutdownNow():停止接受新任务,原来的任务停止执行;能立即停止线程池,正在跑的和正在等待的任务都停下System.out.println("Hello: "+future.get());
打印结果,注意这个get方法是一个阻塞方法,如果任务 还没有执行完,那么会一直阻塞直到任务完成
为了防止阻塞,可以先用isDown()判断是否执行完
如果遇到长时间没有执行完的任务有两种处理方法:future.get(1000, TimeUnit.MILLISECONDS)
方法get(long, TimeUnit)是表示指定超时时间后任务仍未返回,那么就会抛出一个 TimeoutException
或者使用boolean canceled = future.cancel(true)方法取消Future
改造上面例子,加入runnable和定时执行线程,另一个Java多线程extends Thread和implements Runnable文档:https://blog.csdn.net/jc_benben/article/details/106156747
主方法:
public static void main(String[] args) {
//1 创建线程池(执行器池)
ExecutorService Executorspool = Executors.newFixedThreadPool(5);
// 定时任务线程
ScheduledExecutorService executorServiceScheduler = Executors.newSingleThreadScheduledExecutor();
Runnable runnable = new Runnable() {
@Override
public void run() {
List<Future<Integer>> list=new ArrayList<Future<Integer>>();
for (int i = 10; i < 12; i++) {
// ---- 程序A ----
System.out.println(i);
Future future=Executorspool.submit(new testCallable(i));
// ---- 程序A ----
list.add(future);
}
//Executorspool.shutdown();
for (Future<Integer> future : list) {
// 打印结果数据
try {
//1s超时报错
System.out.println("Hello: "+future.get(1000, TimeUnit.MILLISECONDS));
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
/*Thread thread = new Thread(runnable);
thread.start();*/
// 每5秒执行runnable 任务
executorServiceScheduler.scheduleAtFixedRate(runnable,0,5000,TimeUnit.MILLISECONDS);
}
callable类:
import java.util.concurrent.Callable;
public class testCallable implements Callable {
Integer initSum = 0;
public testCallable(Integer initSum) {
this.initSum = initSum;
}
@Override
public Integer call() throws Exception {
int sum=1000;
/*for (int i = 0; i <=10; i++) {
//Thread.sleep(1000); //定义了5个执行器,这里开起了话将会看到效果
sum+=i;
System.out.println("CurrentThread:"+Thread.currentThread());
}*/
System.out.println("CurrentThread:"+Thread.currentThread() +"-----> "+initSum);
return sum + initSum;
}
}
Runnable和Callable的区别:
Runnable执行方法是run(),Callable是call()
实现Runnable接口的任务线程无返回值;实现Callable接口的任务线程能返回执行结果
call方法可以抛出异常,run方法若有异常只能在内部消化