600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > libevent c++高并发网络编程_【多线程高并发编程】Callable源码分析

libevent c++高并发网络编程_【多线程高并发编程】Callable源码分析

时间:2020-01-09 06:42:55

相关推荐

libevent c++高并发网络编程_【多线程高并发编程】Callable源码分析

程序猿学社的GitHub,欢迎Star/ITfqyd/cxyxs

本文已记录到github,形成对应专题。

前言

通过上一章实现多线程有几种方式,我们已经了解多线程实现的方式。但是,一般大家知道的就两种,继承Thread类和实现runnable接口。对Callable接口实现多线程很陌生,我们看通过源码学习,了解callable是怎么一回事。

1.Callable的前世今生

通过runnable接口或者继承Thread实现一个多线程,我们想要获取线程的运行结果,可以通过共享变量的方式来获取。为了解决这个问题,java在jdk1.5中引入callable、Future接口。

1.1 Callable的前世Runnable

package com.cxyxs.thread.three;import java.util.Date;/*** Description:转发请注明来源 程序猿学社 - https://ithub./* Author: 程序猿学社* Date: /2/17 21:43* Modified By:*/public class MyThreadRunnable implements Runnable{//测试账号private int count=0;@Overridepublic void run() {for (int i = 0; i < 5; i++) {try {Thread.sleep(1000);count++;} catch (InterruptedException e) {e.printStackTrace();}}}public static void main(String[] args) {MyThreadRunnable run = new MyThreadRunnable();Thread thread = new Thread(run);thread.start();try {//等待线程执行完毕,不然获取不到最终的结果thread.join();System.out.println(run.count);} catch (InterruptedException e) {e.printStackTrace();}}}

测试结果

通过这种方式,我们是不是实现了,获取线程的结果。现在我们总结一下通过runnable方式,是如何实现获取多线程的返回接口的。定义一个类实现Runnable接口 定义一个成员变量,用来接收run方法运行后,我们需要的结果。调用时,实例化该类,作为形参传入Thread中,调用start方法启动线程调用线程的join方法 ,等待线程执行完毕,这样我们才能取到线程执行完后的结果5,不然就是0.

1.2 Callable的今生

通过FutureTask+Thread调用

package com.cxyxs.thread.three;import java.util.Date;import java.util.concurrent.Callable;import java.util.concurrent.FutureTask;/*** Description:转发请注明来源 程序猿学社 - https://ithub./* Author: 程序猿学社* Date: /2/19 0:12* Modified By:*/public class MyThreadCallable {public static void main(String[] args) throws Exception{//第一步Callable<Integer> callable = new Callable<Integer>() {@Overridepublic Integer call() throws Exception {int count=0;for (int i = 0; i < 5; i++) {Thread.sleep(1200);count++;}return count;}};FutureTask<Integer> task = new FutureTask<>(callable);Thread thread = new Thread(task);thread.start();Integer result = task.get();System.out.println("获取多线程的值:"+result);}}

为了偷懒,这里第一步简写了,一般都是类实现Callable接口,重写call,我这里使用简写的方法实现。实例化一个FutureTask,把实现Callable的类,作为构造方法的形参传入进去。实例化一个线程,把FutureTask这个对象,作为构造方法的形参传入。调用FutureTask对象的get方法。就可以获取值。

ExecutorService+Future调用实现

public void two() throws Exception{Callable<Integer> callable = new Callable<Integer>() {@Overridepublic Integer call() throws Exception {int count=0;for (int i = 0; i < 5; i++) {Thread.sleep(1200);count++;}return count;}};ExecutorService e= Executors.newFixedThreadPool(10);Future<Integer> f1=e.submit(callable);Integer result = f1.get();System.out.println("获取多线程的值:"+result);}

建议,使用这种方式,通过线程池管理多线程。引出一个问题,为什么要使用线程池?

为了减少创建和销毁线程的次数,让每个线程可以多次使用。

这里了解即可。知道有这么一回事。后续会有相关的文章进行线程池的相关介绍。

2.源码分析

话不多说,就按照我们刚刚总结的通过Callable如何实现一个多线程的步骤。我们对ExecutorService+Future调用实现的方式进行分析,下面我们开始一步步得到查看他的源码。有闲情的社友,可以自己试试看看FutureTask+Thread这种方式调用的源码。

2.1 第一步,实现Callable接口

Callable接口

@FunctionalInterfacepublic interface Callable<V> {/*** Computes a result, or throws an exception if unable to do so.** @return computed result* @throws Exception if unable to compute a result*/V call() throws Exception;}

Runnable接口

@FunctionalInterfacepublic interface Runnable {public abstract void run();}

看了源码后,各位社友,能说一说Callable和Runnable接口的区别吗?

方法名不同,Callable是call,Runnable是run Callable是方法,Runnable是抽象方法 Callable抛出异常,Runnable没有抛出异常Callable有返回值V,Runnable没有返回值。Callable可以定义泛型,这样我们通过get调用时,无需拆箱装箱。

而抛出异常和有返回值,就是Callable很重要的一个优点,也就是为了解决Runnable一些痛点问题。

2.2 FutureTask类结构图

首先我们先看看futureTask的类结构。在idea中右键,选择digrams->show digrams(idea版本)

通过这个图,我们能很好的看出对应的关系,C表示是类,I是接口

@FunctionalInterface是函数式接口。

我们根据这个图,一个个进行源码分析。

通过图,我们可以发现FutureTask实现了RunnableFuture接口,让我们一探究竟。

2.3 RunableFuture接口

public interface RunnableFuture<V> extends Runnable, Future<V> {/*** Sets this Future to the result of its computation* unless it has been cancelled.*/void run();}

看到这里,我们也就知道我们之前为什么说Callable是Runnable的plus版本。实际上就是在就是在Runnable的基础上做了一些优化。

2.4 Runnable接口

@FunctionalInterfacepublic interface Runnable {public abstract void run();}

这个因为之前有阐述过,这里就不过多的描述。

2.5 Future接口

通过查看类或者接口下的所有方法(idea中使用alt+7)

cancel方法:还没计算完,可以取消计算过程,isCancelled方法:是否取消isDone方法:是否完成get方法:获取计算结果(如果还没计算完,也是必须等待的)get(long,TimeUnit) 获取计算结果,可以指定一个超时时间,如果在规定时间内,没有返回结果,会抛出一个TimeOutException的异常

2.6 FutureTask源码分析

通过代码,我们发现我们调用FutureTask的构造方法,把callable的实例对象,作为形参,传入进去咯。

public FutureTask(Callable<V> callable) {if (callable == null)throw new NullPointerException();this.callable = callable;this.state = NEW; // ensure visibility of callable}

如果没有传Callable就抛出NullPointerException.我们发现这里有一个变量state的值为NEW

/** Possible state transitions:* NEW -> COMPLETING -> NORMAL* NEW -> COMPLETING -> EXCEPTIONAL* NEW -> CANCELLED* NEW -> INTERRUPTING -> INTERRUPTED*/private volatile int state;private static final int NEW= 0;private static final int COMPLETING = 1;private static final int NORMAL = 2;private static final int EXCEPTIONAL = 3;private static final int CANCELLED = 4;private static final int INTERRUPTING = 5;private static final int INTERRUPTED = 6;

NEW(初始化)COMPLETING(运行中)NORMAL(完成状态)EXCEPTIONAL(任务因异常而结束)CANCELLED(还未运行就调用cancel(true)方法取消)INTERRUPTING(正在运行中就调用cancel(true)方法被取消)INTERRUPTED(被取消状态,由INTERRUPTING转换成INTERRUPTED)

源码注释部分,就是任务的几种状态变化。

一般都是直接把FutureTask对象,作为参数传给Thread类的构造方法传入。

正常情况下NEW -> COMPLETING -> NORMAL

调用new FutureTask把状态改为New

调用FutureTask的run方法改为COMPLETING

中途不发现异常,执行完run方法,就会返回NORMAL

调用task.get可以获取线程执行的结果

public V get() throws InterruptedException, ExecutionException {int s = state;if (s <= COMPLETING)s = awaitDone(false, 0L);return report(s);}

COMPLETING标识运行中,判断这个任务是否已经在运行中,也就是已调用run方法。说明任务还未完成,我们需要等待,所以这里调用了awaitDone方法。

返回结果是调用的report方法

private V report(int s) throws ExecutionException {Object x = outcome;if (s == NORMAL)return (V)x;if (s >= CANCELLED)throw new CancellationException();throw new ExecutionException((Throwable)x);}

如果状态为已完成(NORMAL),直接返回运行结果。

关注 【 @程序猿学社 】回复关键字,查看更多干货。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。