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();
相关推荐
Java提供了两种主要的线程创建方式:继承Thread类和实现Runnable接口。在本项目中,可能选择创建一个实现了Runnable接口的类,然后将其实例传递给Thread构造函数,创建新的线程实例。这样做可以避免单继承的限制,...
在Java多线程编程中,有时我们需要确保所有子线程执行完毕后再进行后续操作,例如在并发测试、数据聚合或资源清理等场景。本篇文章将详细介绍五种在Java中等待所有子线程执行完的方法。 ### 方法一:使用`sleep`...
Executor框架提供了ThreadPoolExecutor,它允许我们预先创建一定数量的线程,管理和控制线程的执行,有效避免了频繁创建和销毁线程的开销。通过调整线程池参数,可以优化系统资源的使用。 异常处理在多线程环境中也...
在Java编程中,多线程是一种重要的技术,它允许程序同时执行多个任务,从而提高效率和性能。在本示例中,“java多线程例子-生产者消费者”旨在展示如何利用多线程来实现生产者和消费者模式。这种模式是并发编程中的...
### Java多线程-JDK5.0新增线程创建方式 #### 一、新增方式1:实现Callable接口 ##### (1)介绍 自Java 5.0起,为提高线程管理的灵活性与效率,引入了`Callable`接口,这是一种全新的创建线程的方式。与传统的`...
hadoop自带的Container-executor在配置yarn-kerberos时存在问题,这边给出编译后的Container-executor,默认加载配置文件路径/etc/container-executor.cfg,大家不用再重新编译了
- **线程的创建**:Java提供了两种创建线程的方式,一是通过实现`Runnable`接口,二是继承`Thread`类。每种方式都有其适用场景,需要根据实际需求选择。 - **生命周期**:线程有新建、就绪、运行、阻塞和终止五种...
读者将学习如何通过实现Runnable接口或继承Thread类来创建线程,以及如何使用Executor框架来管理线程池。 此外,书中还深入探讨了线程安全问题,包括共享资源的并发访问、死锁、活锁、饥饿等问题,以及如何通过同步...
- **Executor框架**:Java提供的线程池服务,可减少线程创建和销毁的开销。 - **ThreadPoolExecutor**:最常见的线程池实现,参数包括核心线程数、最大线程数、队列容量等。 - **ScheduledExecutorService**:...
由于提供的文件内容大部分与Java多线程编程核心技术并无直接关联,而是关于电子书资源的联系方式和说明,因此不能直接从这部分内容中生成关于Java多线程的知识点。但考虑到描述中提到了电子书的标题以及它涉及的主题...
Java多线程编程是Java开发中的重要组成部分,它允许程序同时执行多个任务,极大地提高了程序的效率和响应性。在Java中,多线程主要通过继承Thread类或实现Runnable接口来实现。本教程《Java多线程编程核心技术》将...
在Java编程中,多线程处理是提升程序性能和效率的重要手段,特别是在处理大量数据库数据时。本主题将深入探讨如何使用Java的并发包(java.util.concurrent)来实现多线程对数据库数据的批量处理,包括增、删、改等...
- 使用ExecutorService和Future:Java 5引入的Executor框架提供了一种更灵活的多线程管理方式。 二、线程控制 1. 同步机制:为了避免多线程间的竞态条件,Java提供了synchronized关键字。它可以用于方法或代码块,...
XXL-JOB是一个分布式任务调度平台,而"xxl-job-executor-go-master"则表示这个项目是XXL-JOB的Go语言实现版本的源码仓库。这个仓库是针对XXL-JOB执行器的一个Go语言实现,它使得开发者可以利用Go语言来编写调度任务...
Java的Executor框架是Java 1.5引入的用于管理和控制多线程的一种机制,它旨在解决直接使用`new Thread(…).start()`方法创建线程所带来的问题。在传统的多线程编程中,直接创建和销毁线程会导致大量开销,且无法有效...
Java提供了ExecutorService和ThreadPoolExecutor类来管理线程,创建线程池可以有效控制并发线程的数量,减少线程的创建和销毁开销。线程池的基本使用如下: ```java ExecutorService executor = Executors....
Java 5引入了`Executor`框架,它提供了一种更加灵活的方式来管理和控制线程池,从而有效地管理线程生命周期。 - **示例代码**: ```java ExecutorService executor = Executors.newFixedThreadPool(10); // 创建...
- Executor框架:Java 5引入,用于管理和控制线程池,提高系统性能,避免频繁创建和销毁线程。 - ThreadPoolExecutor:线程池的核心实现,可以通过设置核心线程数、最大线程数、线程存活时间等参数定制线程池行为...
此外,Java还提供了Callable和Future接口,以及Executor框架,用于创建带返回值的线程和管理线程池,这在处理复杂并发场景时非常有用。 在实际编程中,多线程可能会遇到竞态条件、死锁等问题,需要通过适当的同步...