摘要:使用获得返回结果新建个线程,每个线程分别负责累加慢速累加器线程等待线程执行完毕累加的结果运行结束,结果为运行结果第二种方法使用和。
Java 对多线程编程提供了内置的支持并提供了良好的 API,通过使用 Thread 和 Runnable 两个基础类,我们可以很方便的创建一个线程:
Runnable runnable = new Runnable() { @Override public void run() { System.out.println("线程启动"); // 耗时操作 System.out.println("线程结束"); } }; Thread thread = new Thread(runnable); // 创建线程,runnable 作为线程要执行的任务(载体) thread.start(); // 启动线程 thread.join(); // 等待线程执行完毕
{ 题外话开始:
通过 Thread 的类声明:
我们可以知道 Thread 自己也实现了 Runnable 接口,Thread 中 run 方法的实现如下(Thread 启动之后运行的就是 Thread 中的 run 方法):
(target 即构造 Thread 时可传入的 Runnable 对象,不传入即为 null —— 所以继承 Thread 重写其 run 方法也是一种创建线程的方式)
题外话结束 }
Runnable.java 的代码:
Runnable 的 run 方法是不带返回值的,那如果我们需要一个耗时任务在执行完之后给予返回值,应该怎么做呢?
第一种方法:在 Runnable 的实现类中设置一个变量 V,在 run 方法中将其改变为我们期待的结果,然后通过一个 getV() 方法将这个变量返回。
import java.util.*; public class RunnableTest { public static void main(String[] args) throws Exception { System.out.println("使用 Runnable 获得返回结果:"); Listworkers = new ArrayList<>(10); List tasks = new ArrayList<>(10); // 新建 10 个线程,每个线程分别负责累加 1~10, 11~20, ..., 91~100 for (int i = 0; i < 10; i++) { AccumRunnable task = new AccumRunnable(i * 10 + 1, (i + 1) * 10); Thread worker = new Thread(task, "慢速累加器线程" + i); tasks.add(task); workers.add(worker); worker.start(); } int total = 0; for (int i = 0, s = workers.size(); i < s; i++) { workers.get(i).join(); // 等待线程执行完毕 total += tasks.get(i).getResult(); } System.out.println(" 累加的结果: " + total); } static final class AccumRunnable implements Runnable { private final int begin; private final int end; private int result; public AccumRunnable(int begin, int end) { this.begin = begin; this.end = end; } @Override public void run() { result = 0; try { for (int i = begin; i <= end; i++) { result += i; Thread.sleep(100); } } catch (InterruptedException ex) { ex.printStackTrace(System.err); } System.out.printf("(%s) - 运行结束,结果为 %d ", Thread.currentThread().getName(), result); } public int getResult() { return result; } } }
运行结果:
第二种方法:使用 Callable
Callable
Callable.java 的代码:
可以看到参数化类型 V 就是返回的值的类型。
但是查看 Thread 的构造方法,我们发现 Thread 只提供了将 Runnable 作为参数的构造方法,并没有使用 Callable
FutureTask
可以看到它实现了 RunnableFuture
可以看到 RunnableFuture 接口继承了 Runnable 接口,那么 RunnableFuture 接口的实现类 FutureTask 必然会去实现 Runnable 接口 —— 所以 FutureTask 可以用来当 Runnable 使用。查看 FutureTask 的构造方法,发现 FutureTask 有两个构造方法:
第一个构造方法表明我们可以通过一个 Callable 去构造一个 FutureTask —— 而 FutureTask 实现了 Runnable 接口,从而可以将该任务传递给 Thread 去运行;
第二个构造方法是通过一个 Runnable 和一个指定的 result 去构造 FutureTask。
我们再来看看 FutureTask
(事实上,RunnableFuture
Future.java 的源码:
Future
(Future 每个方法的详细用法可以参考 Java 多线程(3))
通过以上我们可以知道,Callable
现在我们看看 FutureTask 中实现 Runnable 的 run 方法是怎样的(我们直接看关键代码):
代码的意思很明确,调用构造 FutureTask
使用 FutureTask
(1)通过一个 Callable
(2)将 FutureTask
(3)使用 FutureTask
现在我们使用 Callable 改写程序:
import java.util.*; import java.util.concurrent.*; public class CallableTest { public static void main(String[] args) throws Exception { System.out.println("使用 Callable 获得返回结果:"); List> futureTasks = new ArrayList<>(10); // 新建 10 个线程,每个线程分别负责累加 1~10, 11~20, ..., 91~100 for (int i = 0; i < 10; i++) { AccumCallable task = new AccumCallable(i * 10 + 1, (i + 1) * 10); FutureTask futureTask = new FutureTask<>(task); futureTasks.add(futureTask); Thread worker = new Thread(futureTask, "慢速累加器线程" + i); worker.start(); } int total = 0; for (FutureTask futureTask : futureTasks) { total += futureTask.get(); // get() 方法会阻塞直到获得结果 } System.out.println("累加的结果: " + total); } static final class AccumCallable implements Callable { private final int begin; private final int end; public AccumCallable(int begin, int end) { this.begin = begin; this.end = end; } @Override public Integer call() throws Exception { int result = 0; for (int i = begin; i <= end; i++) { result += i; Thread.sleep(100); } System.out.printf("(%s) - 运行结束,结果为 %d ", Thread.currentThread().getName(), result); return result; } } }
运行结果:
可以看到使用 Callable
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/66355.html
摘要:返回与此锁相关联的给定条件等待的线程数的估计。查询是否有线程正在等待获取此锁。为公平锁,为非公平锁线程运行了获得锁定运行结果公平锁的运行结果是有序的。 系列文章传送门: Java多线程学习(一)Java多线程入门 Java多线程学习(二)synchronized关键字(1) java多线程学习(二)synchronized关键字(2) Java多线程学习(三)volatile关键字 ...
摘要:本文只介绍中线程池的基本使用,不会过多的涉及到线程池的原理。可缓存线程的线程池创建一个可缓存线程的线程池。首先是从接口继承到的方法使用该方法即将一个任务交给线程池去执行。方法方法的作用是向线程池发送关闭的指令。 首先,我们为什么需要线程池?让我们先来了解下什么是 对象池 技术。某些对象(比如线程,数据库连接等),它们创建的代价是非常大的 —— 相比于一般对象,它们创建消耗的时间和内存都...
摘要:时,标准类库添加了,作为对型线程池的实现。类图用来专门定义型任务完成将大任务分割为小任务以及合并结果的工作。 JDK 1.7 时,标准类库添加了 ForkJoinPool,作为对 Fork/Join 型线程池的实现。Fork 在英文中有 分叉 的意思,而 Join 有 合并 的意思。ForkJoinPool 的功能也是如此:Fork 将大任务分叉为多个小任务,然后让小任务执行,Join...
摘要:典型地,和被用在等待另一个线程产生的结果的情形测试发现结果还没有产生后,让线程阻塞,另一个线程产生了结果后,调用使其恢复。使当前线程放弃当前已经分得的时间,但不使当前线程阻塞,即线程仍处于可执行状态,随时可能再次分得时间。 1、说说进程,线程,协程之间的区别 简而言之,进程是程序运行和资源分配的基本单位,一个程序至少有一个进程,一个进程至少有一个线程.进程在执行过程中拥有独立的内存单元...
摘要:线程执行框架启动线程将要多线程执行的任务封装为一个对象将其传给一个执行框架对象从线程池中选择线程执行工作任务。 为什么需要执行框架呢?使用一般的new方法来创建线程有什么问题呢?一般的new线程的方式一般要给出一个实现了Runnable接口的执行类,在其中重写run()方法,然后再在将这个执行类的对象传给线程以完成初始化,这个过程中线程的定义和执行过程其实是杂糅在一起了,而且每次new...
阅读 767·2023-04-25 15:13
阅读 1388·2021-11-22 12:03
阅读 816·2021-11-19 09:40
阅读 1897·2021-11-17 09:38
阅读 1702·2021-11-08 13:18
阅读 649·2021-09-02 15:15
阅读 1760·2019-08-30 15:54
阅读 2623·2019-08-30 11:12