Java 并发编程—— Executors 分析应用,java面试题中级

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
img

正文

this.state = NEW; // ensure visibility of callable
}

所以在后续的执行中就调用的是 callbale 对象的 call 方法执行,并将结果存储在运行的 FutureTask 中进行返回,正常获取。

如果传入的类型是 Runnable,同样调用 newTaskFor 方法生成 FutureTask 对象。

public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}

public static Callable callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter(task, result);
}

static final class RunnableAdapter implements Callable {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}

可以看到,经过一系列的转折,最终是转换成一个 RunnableAdapter,这个 RunnableAdapter 的值就是传入的 result,没传入就是 null

针对 Runnable 类型参数概括一下,这段可能比较绕,所以多结合源码理解下过程:

  1. submit 方法中传入 Runnable 类型,一般为了获取结果,会将 Callable 对象构建成 FutureTask 类型在传入,(此处记作 FutureA);
  2. 调用 newTaskFor 方法生成 FutureTask 对象(记作 FutureB),这个对象就是我们 submit 方法返回的 Future 对象;
  3. FutureTask 的构造方法中调用 Executors.callable(runnable, result) 方法构建一个 Callable 对象存储在 FutureTask(即 FutureB) 的成员变量 callable 中。其中 result 默认为 null,由于传入的是 Runnable 类型,所以在构建的时候是通过新建一个 Callable 的子类 RunnableAdapter 进行封装。
  4. task 任务经过入队成功开始执行的时候,就是执行的 callablecall 方法。由于当前的 Callable 对象是 RunnableAdapter 类型,所以最终是调用传入的 RunnableFutureTask 类型)的 run 方法,并且返回值是 result
  5. 经过这样的一波三折,最终回到构建原始的 FutureTaskCallable 中调用 call 方法,计算结果就被存储在传入作为参数的 FutureTask 中,而返回值的 Future 结果就是 result

所以在 FutureTask + Callable 结合使用时,如果通过 submit 返回值来获取计算结果就会出现为 null 的情况。

ThreadPoolExecutor 的介绍中,我们针对 execute 进行了大致的流程介绍,并没有涉及到实际的执行流程,所以在这里我们针对 submit 方法的执行捋一遍流程。

2. addWorker

private boolean addWorker(Runnable firstTask, boolean core) {
/**

  • 此处省略 n 行代码,它们的主要作用是判断线程池的状态是否是运行状态,以及线程数是否超标。
  • 如果线程池是运行状态,并且线程没超标,则往下执行创建线程。
    */

boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
// 将 firstTask 封装成 Worker对象。
w = new Worker(firstTask);
// 获取封装的任务线程
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 重新检查线程池的状态
int rs = runStateOf(ctl.get());

if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
// 线程池是运行状态 或 是关闭状态但是没任务,则添加 work 到集合
workers.add(w);
// 获取添加后的集合大小,修改 poolSize 的值
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
// 成功添加,开启线程执行
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}

addWorkder 的逻辑大致可以分为以下步骤:

  1. 它们的主要作用是判断线程池的状态是否是运行状态,以及线程数是否超标。如果线程池是运行状态,并且线程没超标,则往下执行创建线程。
  2. 创建 Worker 对象。
  3. 添加 Worker 对象到集合。
  4. 获取 Worker 线程并执行。

我们想要获得最终的执行转换,如何转到我们定义的接口,就需要扒下 Worker 的外衣来看看。

private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
private static final long serialVersionUID = 6138294804551838833L;

/** Thread this worker is running in. Null if factory fails. /
final Thread thread;
/
* Initial task to run. Possibly null. /
Runnable firstTask;
/
* Per-thread task counter */
volatile long completedTasks;

/**

  • Creates with given first task and thread from ThreadFactory.
  • @param firstTask the first task (null if none)
    */
    Worker(Runnable firstTask) {
    setState(-1); // inhibit interrupts until runWorker
    this.firstTask = firstTask;
    this.thread = getThreadFactory().newThread(this);
    }

/** Delegates main run loop to outer runWorker */
public void run() {
runWorker(this);
}

// 省略 n 行代码
}

首先需要明确 Worker 类是 ThreadPoolExecutor 的内部类。Worker 类是集成 AbstractQueuedSynchronizer 的子类,AbstractQueuedSynchronizer 应该都熟悉了(前面我们在并发编程锁的系列介绍过),同时实现了 Runnable 接口。它的内部包含一个 RunnableThread 对象,而这个 Thread 对象是通过创建。

this.thread = getThreadFactory().newThread(this);

将自身作为一个参数进行创建。getThreadFactory() 方法 ThreadPoolExecutor 提供的获取 ThreadFactory 方法,最终的实现是在 Executor 的内部类 DefaultThreadFactory 中进行实现。

static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;

DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = “pool-” +
poolNumber.getAndIncrement() +
“-thread-”;
}

public Thread newThread(Runnable r) {
// 将我们的任务 Runnable 对象作为参数常见 Thread
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
// 判断是否是守护线程,如果是守护线程,设为为 false,让它不是守护线程
if (t.isDaemon())
t.setDaemon(false);
// 设置线程的优先级为普通
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}

这样 Worker 以自身为参数创建一个线程,当线程启动的时候就会执行 workerrun 方法。最终执行到 runWorker(this)

final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
// 获取 Worker 封装的 task 任务
Runnable task = w.firstTask;
// 销毁 workder 对象的 Runnable 引用,并释放锁
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
// 在任务执行前,先获取锁,确保线程执行的时候线程池不被打断
w.lock();
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
// 在 task 执行前,调用 beforeExecute 方法抛出异常,task 不会执行。该方法是空。
beforeExecute(wt, task);
Throwable thrown = null;
try {
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}

这里的关键方法 task.run(),由于 taskFutureTask 类型,所以程序运行到 FutureTaskrun 方法中。

public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
// 调用自定义的 callable 对象的 call 方法,获取计算结果
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
// 设置计算结果返回值
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}

protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}

最终,所有的执行和结果存储都回归到 FutureTask 中。

addworker.png 至此,整个的流程逻辑分析完毕。

3. 简单应用

下面通过一些简单的实例模拟下如何使用,主要有:

  • Future + Callable
  • FutureTask + Callable
3.1 Future + Callable

public class HelloWorld {

Future feature;
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
Callables callable = new Callables();
Future future = executorService.submit(callable);
executorService.shutdown();
try {
System.out.println(“主线程获取结果:” + future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}

static class Callables implements Callable{

@Override
public Integer call() throws Exception {
Thread.sleep(1000);
int sum =0;
for(int i=0;i<100;i++) {
sum += i;
}
System.out.println(“子线程计算结果:” + sum);
return sum;
}
}
}

执行结果:

子线程计算结果:4950
主线程获取结果:4950

3.2 FutureTask + Callable

public class HelloWorld {

Future feature;
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
Callables callable = new Callables();
FutureTask futureTask = new FutureTask<>(callable);
Future<?> future = executorService.submit(futureTask);
executorService.shutdown();
try {
System.out.println(“主线程获取结果:” + future.get() + “==” + futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}

static class Callables implements Callable{

@Override
public Integer call() throws Exception {
Thread.sleep(1000);
int sum =0;
for(int i=0;i<100;i++) {
sum += i;
}
System.out.println(“子线程计算结果:” + sum);
return sum;
}
}
}
执行结果:
子线程计算结果:4950
主线程获取结果:null==4950
可以看到,我们通过 submit 返回的 Future 获取的结果是 null。
这篇文章之后,并发编程的文章也算告一段落,当然还有很多没有涉及到,后面有时间在继续吧。

最后

即使是面试跳槽,那也是一个学习的过程。只有全面的复习,才能让我们更好的充实自己,武装自己,为自己的面试之路不再坎坷!今天就给大家分享一个Github上全面的Java面试题大全,就是这份面试大全助我拿下大厂Offer,月薪提至30K!

我也是第一时间分享出来给大家,希望可以帮助大家都能去往自己心仪的大厂!为金三银四做准备!
一共有20个知识点专题,分别是:

Dubbo面试专题

JVM面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

Java并发面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

Kafka面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

MongDB面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

MyBatis面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

MySQL面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

Netty面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

RabbitMQ面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

Redis面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

Spring Cloud面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

SpringBoot面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

zookeeper面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

常见面试算法题汇总专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

计算机网络基础专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

设计模式专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

转存中…(img-mvym6Yzq-1713607907230)]

常见面试算法题汇总专题

[外链图片转存中…(img-U1OBs1tu-1713607907231)]

计算机网络基础专题

[外链图片转存中…(img-bh1fcZwr-1713607907231)]

设计模式专题

[外链图片转存中…(img-6FaD8ekO-1713607907232)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-Z9RlT8tm-1713607907232)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 28
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值