JAVA多线程实现方式主要有三种:继承Thread类、实现Runnable接口、使用Executor框架(JDK 1.5中引入)。
// 方式一:继承Thread类,重写run()方法 Thread thread = new Thread() { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("开始"); } }; thread.start(); System.out.println("111");
// 方式二:实现Runnable接口 Thread thread = new Thread(new Runnable() { public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("开始"); } }); thread.start(); System.out.println("111");
输出的结果都是:(先输出111,再输出“开始”)
111
开始
传统线程两种实现方式的区别:
可以查看《Java中有两种实现多线程的方式以及两种方式之间的区别》文章。
实现Runnable接口相对于继承Thread类来说,有如下显著的好处:
(1)适合多个相同程序代码的线程去处理同一资源的情况,把虚拟CPU(线程)同程序的代码,数据有效的分离,较好地体现了面向对象的设计思想。
(2)可以避免由于Java的单继承特性带来的局限。我们经常碰到这样一种情况,即当我们要将已经继承了某一个类的子类放入多线程中,由于一个类不能同时有两个父类,所以不能用继承Thread类的方式,那么,这个类就只能采用实现Runnable接口的方式了。
(3)有利于程序的健壮性,代码能够被多个线程共享,代码与数据是独立的。当多个线程的执行代码来自同一个类的实例时,即称它们共享相同的代码。多个线程操作相同的数据,与它们的代码无关。当共享访问相同的对象是,即它们共享相同的数据。当线程被构造时,需要的代码和数据通过一个对象作为构造函数实参传递进去,这个对象就是一个实现了Runnable接口的类的实例。
下面重点讲解Executor框架
Executor框架是指java 5中引入的一系列并发库中与executor相关的一些功能类,其中包括线程池,Executor,Executors,ExecutorService,CompletionService,Future,Callable等。
用Executor框架可以实现子线程与主线程的“通信”,能够传递数据。
Executors类,提供了一系列工厂方法用于创先线程池,返回的线程池都实现了ExecutorService接口。
直接看代码
Executor executor = Executors.newFixedThreadPool(10); Runnable task = new Runnable() { public void run() { System.out.println("task executing"); } }; executor.execute(task); System.out.println("111");
输出结果:
111
task executing
子线程会一直处于运行状态。
更换Executor 接口为ExecutorService 接口,可以调用executor.shutdown(); 在子线程运行完毕之后,结束子线程的运行。
ExecutorService executor = Executors.newFixedThreadPool(10);//创建10个线程池 Runnable task = new Runnable() { public void run() { System.out.println("task executing"); } }; executor.execute(task); System.out.println("111"); executor.shutdown();//运行结束,关闭线程池
ExecutorService 扩充了Executor 接口的方法,比较常见的实现类是ThreadPoolExecutor,详细用法可参考API手册。
shutdown()
启动一次顺序关闭,执行以前提交的任务,但不接受新任务。
public static ExecutorService newFixedThreadPool(int nThreads)
创建固定数目线程的线程池。
public static ExecutorService newCachedThreadPool()
创建一个可缓存的线程池,调用execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。
public static ExecutorService newSingleThreadExecutor()
创建一个单线程化的Executor。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。
Executor executor = Executors.newFixedThreadPool(10); Runnable task = new Runnable() { public void run() { System.out.println("task executing"); } }; executor.execute(task); ScheduledExecutorService scheduler = (ScheduledExecutorService) Executors .newScheduledThreadPool(10); scheduler.scheduleAtFixedRate(task, 5, 1, TimeUnit.SECONDS);
输出结果是:
task executing
task executing
task executing
程序会先输出 task executing,过5秒之后,再次输出 task executing ,然后,每间隔1秒输出 task executing
关于多线程Callable和Future
从java 5开始,Java提供了Callable接口,该接口是Runnable接口的增强版,Callable接口提供了一个call()方法,可以作为线程执行体,但call()方法比run()方法的功能更强大:有返回值,可以声明抛出异常
因此我们完全可以提供一个Callable对象作为Thread的target,而该线程的线程执行体就是Callable对象的call()方法。
在工具开发包中,java 5 提供了Future接口来代表Callable接口里的call()方法的返回值,并且为Future接口提供了一个FutureTask实现类,
该实现类实现了Future接口,并且实现了Runnable接口——可以作为Thread类的target。
实际上这里的FutureTask类实现了一个桥梁的作用,用于连接Callable对象和Thread对象。
需要注意的是Callable接口有泛型限制,Callable接口里的泛型形参类型,与call()方法的返回值类型相同。
创建并启动有返回值的线程步骤与实现Runnable接口相似:
1.创建Callable接口的实现类,并实现call()方法
2.创建Callable实现类的实例,使用FutureTask类来包装Callable对象
3.使用FutrueTask对象作为Thread对象的target创建并启动新线程。
4.调用FutrueTask对象的get()方法来获得子线程执行结束后的返回值。
public class ThirdThread implements Callable<Integer> { public Integer call() { int i = 0; for (; i < 30; i++) { System.out.println(Thread.currentThread().getName() + "循环变量i的值是 : " + i); } return i; } public static void main(String[] args) { ThirdThread rt = new ThirdThread(); // 创建Callable对象 FutureTask<Integer> task = new FutureTask<Integer>(rt);// 使用FutrueTask来包装Callable对象 for (int i = 0; i < 30; i++) { System.out .println(Thread.currentThread().getName() + "-------" + i); if (i == 4) { new Thread(task, "有返回值的线程").start(); // 实际上还是以Callable对象创建并启动线程 } } try { System.out.println("子线程的返回值 :" + task.get()); } catch (Exception e) { e.printStackTrace(); } } }
分析:当 i=4 时,主线程与子线程争夺资源 ,子线程名称更改为“有返回值的线程”
子线程 从 0 到 30 开始循环输出,直到i=30退出,将值返回给主线程,
主线程执行 task.get() 方法时,等待子线程计算完成,然后获取其结果。
输出结果:
main-------0
main-------1
main-------2
main-------3
main-------4
main-------5
main-------6
main-------7
main-------8
main-------9
main-------10
main-------11
main-------12
main-------13
main-------14
main-------15
main-------16
main-------17
有返回值的线程循环变量i的值是 : 0
main-------18
main-------19
main-------20
main-------21
main-------22
有返回值的线程循环变量i的值是 : 1
main-------23
有返回值的线程循环变量i的值是 : 2
main-------24
有返回值的线程循环变量i的值是 : 3
main-------25
有返回值的线程循环变量i的值是 : 4
main-------26
有返回值的线程循环变量i的值是 : 5
main-------27
有返回值的线程循环变量i的值是 : 6
main-------28
有返回值的线程循环变量i的值是 : 7
main-------29
有返回值的线程循环变量i的值是 : 8
有返回值的线程循环变量i的值是 : 9
有返回值的线程循环变量i的值是 : 10
有返回值的线程循环变量i的值是 : 11
有返回值的线程循环变量i的值是 : 12
有返回值的线程循环变量i的值是 : 13
有返回值的线程循环变量i的值是 : 14
有返回值的线程循环变量i的值是 : 15
有返回值的线程循环变量i的值是 : 16
有返回值的线程循环变量i的值是 : 17
有返回值的线程循环变量i的值是 : 18
有返回值的线程循环变量i的值是 : 19
有返回值的线程循环变量i的值是 : 20
有返回值的线程循环变量i的值是 : 21
有返回值的线程循环变量i的值是 : 22
有返回值的线程循环变量i的值是 : 23
有返回值的线程循环变量i的值是 : 24
有返回值的线程循环变量i的值是 : 25
有返回值的线程循环变量i的值是 : 26
有返回值的线程循环变量i的值是 : 27
有返回值的线程循环变量i的值是 : 28
有返回值的线程循环变量i的值是 : 29
子线程的返回值 :30
class MyCallable implements Callable<Object> { private String taskNum; MyCallable(String taskNum) { this.taskNum = taskNum; } public Object call() throws Exception { System.out.println(">>>" + taskNum + "任务启动"); Thread.sleep(1000); System.out.println(">>>" + taskNum + "任务终止"); return taskNum + "任务返回运行结果"; } }
public class Test { public static void main(String[] args) throws ExecutionException, InterruptedException { System.out.println("----程序开始运行----"); int taskSize = 5; // 创建一个线程池 ExecutorService pool = Executors.newFixedThreadPool(taskSize); // 创建多个有返回值的任务 List<Future> list = new ArrayList<Future>(); for (int i = 0; i < taskSize; i++) { Callable c = new MyCallable(i + " "); // 执行任务并获取Future对象 Future f = pool.submit(c); list.add(f); } // 关闭线程池 pool.shutdown(); // 获取所有并发任务的运行结果 for (Future f : list) { // 从Future对象上获取任务的返回值,并输出到控制台 System.out.println(">>>" + f.get().toString()); } System.out.println("----程序结束运行----"); } }
ExecutoreService提供了submit()方法,传递一个Callable,或Runnable,返回Future。如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。
执行结果:
----程序开始运行----
>>>2 任务启动
>>>1 任务启动
>>>3 任务启动
>>>0 任务启动
>>>4 任务启动
>>>2 任务终止
>>>1 任务终止
>>>3 任务终止
>>>0 任务终止
>>>0 任务返回运行结果
>>>1 任务返回运行结果
>>>2 任务返回运行结果
>>>3 任务返回运行结果
>>>4 任务终止
>>>4 任务返回运行结果
----程序结束运行----
定时执行
class Task implements Callable<String> { public String call() throws Exception { System.out.println("task executing"); return "success"; } }
客户端调用
public static void main(String[] args) throws InterruptedException, ExecutionException { ScheduledThreadPoolExecutor executor = (ScheduledThreadPoolExecutor) Executors .newScheduledThreadPool(10); ScheduledFuture<String> schedule = executor.schedule(new Task(), 1, TimeUnit.SECONDS); String retVal = schedule.get(); System.out.println(retVal); executor.shutdown(); }
启动之后,1秒执行,输出结果:
task executing
success
在executor.shutdown()方法执行执行的时候,如果还有任务没有执行完成,可以执行
executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
“一盆凉水”将剩余的任务“浇灭”
再看下面的例子
class Task implements Callable<String> { private String taskNum; public Task(String taskNum) { this.taskNum = taskNum; } public Task() { } public String call() throws Exception { System.out.println("task " + taskNum + " executing"); return "success"; } }
返回第一个结果
// 创建一个线程池 ExecutorService pool = Executors.newFixedThreadPool(5); List tasks = new ArrayList<Task>(); tasks.add(new Task("1111")); tasks.add(new Task("2222")); String result = pool.invokeAny(tasks); System.out.println(result.toString()); // 关闭线程池 pool.shutdown();
返回所有结果
// 创建一个线程池 ExecutorService pool = Executors.newFixedThreadPool(5); List tasks = new ArrayList<Task>(); tasks.add(new Task("1111")); tasks.add(new Task("2222")); List<Future<MyCallable>> result = pool.invokeAll(tasks); for (Future<MyCallable> future : result) { System.out.println(future.get()); } // 关闭线程池 pool.shutdown();
相关推荐
一、为什么要引入Executor框架? 1、如果使用new Thread(…).start()的方法处理多线程,有如下缺点: ① 开销大。对于JVM来说,每次新建线程和销毁线程都会有很大的开销。 ② 线程缺乏管理。没有一个池来限制线程的...
hadoop版本3.2.1 hadoop自带的Container-executor在配置yarn-kerberos时存在问题,以及在配置cgroup时需要把container-executor.cfg的上级目录...2 创建/etc/hadoop目录,并将container-executor.cfg放在该目录下即可
Java是天生就支持并发的语言,支持并发意味着多线程,线程的频繁创建在高并发及大数据量是非常消耗资源的,因为java提供了线程池。这篇文章主要介绍下并发包下的Executor接口,Executor接口虽然作为一个非常旧的接口...
xxl-job-executor-go-master
1. Java多线程学习(一)Java多线程入门 2. Java多线程学习(二)synchronized关键字(1) 3. Java多线程学习(二)synchronized关键字(2) 4. Java多线程学习(三...9. Java多线程学习(八)线程池与Executor 框架
hadoop自带的Container-executor在配置yarn-kerberos时存在问题,这边给出编译后的Container-executor,默认加载配置文件路径/etc/container-executor.cfg,大家不用再重新编译了
go-xxl-executor-master
azkaban-executor-server-2.5.0-tar.gz azkaban-web-server-2.5.0-tar.gz azkaban-sql-script-2.5.0-tar.gz
go-executor-example-master
Executor框架是Java并发编程中的一个重要工具,它提供了一种管理线程池的方式,使得我们可以更方便地管理线程的生命周期和执行线程任务。 原子操作是指不可被中断的操作,要么全部执行成功,要么全部不执行。原子...
1、azkaban介绍、三种(solo-server、two-server和multiple-executor)部署方式及验证 网址:https://blog.csdn.net/chenwewi520feng/article/details/130728956 介绍阿兹卡班的主要功能、应用场景以及三种部署方式...
azkaban-executor-2.5.0.tar.gz azkaban-executor-2.5.0.tar.gz azkaban-executor-2.5.0.tar.gz
2.编译prometheus-am-executor二进制文件 go test -count 1 -v ./... go build 用法 Usage: ./prometheus-am-executor [options] script [args..] -f string YAML config file to use -l string ...
高效率 快捷操作
该文档详细记录了Executor框架结构、使用示意图、ThreadPoolExecutor使用示例、线程池原理分析、几种常见线程池(FixedThreadPool、SingleThreadExecutor、CachedThreadPool)的详解以及线程池大小确定等内容
datax
mybatis中的sqlsession--executor实现 mybatis中的sqlsession--executor实现
xxl-job-executor的gin中间件背景xxl-job-executor-go是xxl-job的golang执行器,可以独立运行,有时候我们要与项目或者框架(如:gin框架)集成起来合并为一个服务,本项目因此而生。执行器项目地址与gin集成示例...
xxl-job-executor-sample-springboot-2.2.0.jar 与xxl-job配套的执行器包,用于 Docker-compose搭建xxl-job(并配置Python3环境xxl-job执行器) 中相应的文件