2024年腾讯一面 Java Phaser 并发编程的理解与应用,腾讯T3大牛亲自讲解

最后

很多程序员,整天沉浸在业务代码的 CRUD 中,业务中没有大量数据做并发,缺少实战经验,对并发仅仅停留在了解,做不到精通,所以总是与大厂擦肩而过。

我把私藏的这套并发体系的笔记和思维脑图分享出来,理论知识与项目实战的结合,我觉得只要你肯花时间用心学完这些,一定可以快速掌握并发编程。

不管是查缺补漏还是深度学习都能有非常不错的成效,需要的话记得帮忙点个赞支持一下

整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

Phaser 详解



通过以上简单例子已知道 Phaser 的作用了。其实它的作用不止这些。

可以动态调整注册任务的数量(最大注册的任务数量为65535)。任务可以在任何时间注册(registerbulkRegister 方法,或者以构造方法形式注册初始任务数量),也可以在任何到达时注销注册的任务(arriveAndDeregister 方法)。

package com.chenpi;

import java.util.concurrent.Phaser;

/**

  • @Description 任务,代表小雪

  • @Author 陈皮

  • @Date 2021/7/4

  • @Version 1.0

*/

public class XiaoXueTask implements Runnable {

private Phaser phaser;

public XiaoXueTask(Phaser phaser) {

this.phaser = phaser;

}

@Override

public void run() {

System.out.println(Thread.currentThread().getName() + " 买好鲍鱼了…");

// 第一阶段的事干完了,等待其他人完成才能进入下一阶段

phaser.arriveAndAwaitAdvance();

System.out.println(Thread.currentThread().getName() + " 炒好鲍鱼了…");

// 第二阶段的事干完了,小雪有事先走了

phaser.arriveAndDeregister();

System.out.println(Thread.currentThread().getName() + “有事先走了”);

}

}

我们修改小雪这个任务,她干完第二阶段的事情就有事先走了,即注销任务。结果如下:

陈皮 买好猪肉了…

小美 买好白菜了…

小雪 买好鲍鱼了…

第一阶段,买食材完成啦!总共参与人数:3

小雪 炒好鲍鱼了…

小雪有事先走了

陈皮 炒好猪肉了…

小美 炒好白菜了…

第二阶段,炒菜完成啦!总共参与人数:2

小美 吃饱了…

陈皮 吃饱了…

第三阶段,吃完饭啦!总共参与人数:2

注册和注销只影响内部计数,内部没有记录具体的注册任务,所以不能查询哪个任务是否已注册。但是我们可以编写 Phaser 的子类来实现记录注册的具体任务。

package com.chenpi;

import java.util.ArrayList;

import java.util.List;

import java.util.concurrent.Phaser;

/**

  • @Description 吃饭阶段器

  • @Author 陈皮

  • @Date 2021/7/4

  • @Version 1.0

*/

public class DiningPhaser extends Phaser {

// 记录注册的任务

private List registeredTask = new ArrayList<>();

public int register(Runnable task) {

registeredTask.add(task);

return super.register();

}

public List getRegisteredTask() {

return registeredTask;

}

/**

  • 每一阶段到达时,都会执行这个方法,执行完后 phase 自动加1,代表进入下一阶段

  • @param phase 代表哪个阶段,从0开始

  • @param registeredParties 注册的任务

  • @return 是否终止

*/

@Override

protected boolean onAdvance(int phase, int registeredParties) {

switch (phase) {

case 0:

System.out.println(“第一阶段,买食材完成啦!总共参与人数:” + registeredParties);

return false;

case 1:

System.out.println(“第二阶段,炒菜完成啦!总共参与人数:” + registeredParties);

return false;

case 2:

System.out.println(“第三阶段,吃完饭啦!总共参与人数:” + registeredParties);

return false;

default:

return true;

}

}

}

测试类中,每一个任务单独注册并记录。

package com.chenpi;

/**

  • @Description

  • @Author 陈皮

  • @Date 2021/7/4

  • @Version 1.0

*/

public class ChenPiMain {

public static void main(String[] args) {

// 创建吃饭阶段器,注册3个任务(人)

DiningPhaser diningPhaser = new DiningPhaser();

// 三个人同时开始干活

Thread thread1 = new Thread(new ChenPiTask(diningPhaser));

thread1.setName(“陈皮”);

diningPhaser.register(thread1);

thread1.start();

Thread thread2 = new Thread(new XiaoMeiTask(diningPhaser));

thread2.setName(“小美”);

diningPhaser.register(thread2);

thread2.start();

Thread thread3 = new Thread(new XiaoXueTask(diningPhaser));

thread3.setName(“小雪”);

diningPhaser.register(thread3);

thread3.start();

System.out.println(“注册的任务:” + diningPhaser.getRegisteredTask());

}

}

启动服务,打印了在阶段器中注册的任务。

陈皮 买好猪肉了…

注册的任务:[Thread[陈皮,5,main], Thread[小美,5,main], Thread[小雪,5,main]]

小美 买好白菜了…

小雪 买好鲍鱼了…

第一阶段,买食材完成啦!总共参与人数:3

小雪 炒好鲍鱼了…

陈皮 炒好猪肉了…

小美 炒好白菜了…

第二阶段,炒菜完成啦!总共参与人数:3

陈皮 吃饱了…

小雪 吃饱了…

小美 吃饱了…

第三阶段,吃完饭啦!总共参与人数:3

对于同步性质,像 CyclicBarrier 一样,Phaser 可以重复等待。Phaser 的 arriveAndAwaitAdvance 方法的作用类似于 CyclicBarrier 的 await 方法。

每一个 Phaser 对象都会关联一个阶段数。这个数从0开始,当所有注册的任务都到达每一个阶段的时候,这个数就递增一次。特别地,如果这个数到达 Integer.MAX_VALUE 后就会重新变回到0。

arrivearriveAndDeregister 方法记录到达,这2个方法不会阻塞,它们会返回已到达的阶段数。

arrive 方法表示当前任务已到达某个阶段,但是不会等待其他任务到达此阶段。arriveAndDeregister 方法表示当前任务已到达某个阶段,并且注销任务。

在每一个阶段中,当所有任务都到达的时候,onAdvance 方法会被最后一个触发阶段到达的任务执行,然后进入下一个阶段。onAdvance 方法可以控制一个 Phaser 的终止,如果我们的 Phaser 对象是继承 Phaser 的子类,可以重写 onAdvance 方法,在每一个阶段到达时这个方法就会被调用从而在每一个阶段做我们想做的事情。

package com.chenpi;

import java.util.concurrent.Phaser;

/**

  • @Description 吃饭阶段器

  • @Author 陈皮

  • @Date 2021/7/4

  • @Version 1.0

*/

public class DiningPhaser extends Phaser {

/**

  • 每一阶段到达时,都会执行这个方法,执行完后 phase 自动加1,代表进入下一阶段

  • @param phase 代表哪个阶段,从0开始

  • @param registeredParties 注册的任务

  • @return 是否终止

*/

@Override

protected boolean onAdvance(int phase, int registeredParties) {

System.out.println(Thread.currentThread().getName() + " 调用了onAdvance方法");

switch (phase) {

case 0:

System.out.println(“第一阶段,买食材完成啦!总共参与人数:” + registeredParties);

return false;

case 1:

System.out.println(“第二阶段,炒菜完成啦!总共参与人数:” + registeredParties);

return false;

case 2:

System.out.println(“第三阶段,吃完饭啦!总共参与人数:” + registeredParties);

return false;

default:

return true;

}

}

}

我们在 onAdvance 方法中打印当前线程,结果表明确实是最后一个触发阶段到达的任务执行 onAdvance 方法,如下:

陈皮 买好猪肉了…

小美 买好白菜了…

小雪 买好鲍鱼了…

小雪 调用了onAdvance方法

第一阶段,买食材完成啦!总共参与人数:3

小雪 炒好鲍鱼了…

陈皮 炒好猪肉了…

小美 炒好白菜了…

小美 调用了onAdvance方法

第二阶段,炒菜完成啦!总共参与人数:3

小美 吃饱了…

陈皮 吃饱了…

小雪 吃饱了…

小雪 调用了onAdvance方法

第三阶段,吃完饭啦!总共参与人数:3

phaser 可以随时被终止。当终止时,所有同步方法(例如 arriveAndAwaitAdvance )会立即返回而不用阻塞等待,并且返回一个负数。同样地,被终止后无法再注册任务。isTerminated 方法可以判断是否已经终止。

phaser 可以在 onAdvance 方法中返回 true 来达到终止的效果。例如我们继承 Phaser 编写的子类可以重写此方法,当到达某个阶段后返回 true 来终止阶段器。

package com.chenpi;

import java.util.concurrent.Phaser;

/**

  • @Description 吃饭阶段器

  • @Author 陈皮

  • @Date 2021/7/4

  • @Version 1.0

*/

public class DiningPhaser extends Phaser {

/**

  • 每一阶段到达时,都会执行这个方法,执行完后 phase 自动加1,代表进入下一阶段

  • @param phase 代表哪个阶段,从0开始

  • @param registeredParties 注册的任务

  • @return 是否终止

*/

@Override

protected boolean onAdvance(int phase, int registeredParties) {

System.out.println(Thread.currentThread().getName() + " 调用了onAdvance方法");

switch (phase) {

case 0:

System.out.println(“第一阶段,买食材完成啦!总共参与人数:” + registeredParties);

return false;

case 1:

System.out.println(“第二阶段,炒菜完成啦!总共参与人数:” + registeredParties);

return false;

case 2:

System.out.println(“第三阶段,吃完饭啦!总共参与人数:” + registeredParties);

return false;

default:

return true;

}

}

}

默认的 onAdvance 方法实现是当注册的任务注销到0的时候返回 true,源码如下:

protected boolean onAdvance(int phase, int registeredParties) {

return registeredParties == 0;

}

phaser 的 forceTermination 方法其实也可以强制终止阶段器。还是以上述例子,小美干完第一阶段的事情后,觉得这样等来等去太费时间了,所以终止这个阶段器。

package com.chenpi;

import java.util.concurrent.Phaser;

/**

  • @Description 任务,代表小美

  • @Author 陈皮

  • @Date 2021/7/4

  • @Version 1.0

*/

public class XiaoMeiTask implements Runnable {

private Phaser phaser;

public XiaoMeiTask(Phaser phaser) {

this.phaser = phaser;

}

@Override

public void run() {

System.out.println(Thread.currentThread().getName() + " 买好白菜了…");

// 第一阶段的事干完了,等待其他人完成才能进入下一阶段

phaser.arriveAndAwaitAdvance();

System.out.println(Thread.currentThread().getName() + " 炒好白菜了…");

// 小美觉得这样等来等去太费时间了,所以终止这个阶段器

phaser.forceTermination();

System.out.println(Thread.currentThread().getName() + " 吃饱了…");

// 第三阶段的事干完了,等待其他人完成才能进入下一阶段

phaser.arriveAndAwaitAdvance();

}

}

再执行程序,结果第一阶段后,后续的任务执行就不再按阶段来了,结果如下:

陈皮 买好猪肉了…

小美 买好白菜了…

小雪 买好鲍鱼了…

小雪 调用了onAdvance方法

第一阶段,买食材完成啦!总共参与人数:3

小雪 炒好鲍鱼了…

小美 炒好白菜了…

小美 吃饱了…

小雪 吃饱了…

陈皮 炒好猪肉了…

陈皮 吃饱了…

Phaser 支持层次结构,可以通过构造函数 Phaser(Phaser parent) 和 Phaser(Phaser parent, int parties) 创建一个树形结构的阶段器。这样可以减轻在一个 Phaser 上注册过多的任务而导致的竞争,从而提升吞吐量,缺点是会增加单个操作的开销。

你会发现 arriveAndAwaitAdvance 方法没有抛出 InterruptedException,即使当前线程被中断,这个方法也不会返回,而是继续等待。所以如果希望在等待时可中断,或者可超时,则可以选择使用以下方法:

// 当当前阶段值等于这个方法的参数phase时,等待;不相等时候立即返回。

awaitAdvanceInterruptibly(int phase)

// 当当前阶段值等于这个方法的参数phase时,等待;不相等时候立即返回。超时会抛出超时异常

awaitAdvanceInterruptibly(int phase, long timeout, TimeUnit unit)

下面罗列一些其他常用方法:

// 获取当前到达的任务数

int getArrivedParties()

// 获取上级Phaser

Phaser getParent()

// 获取当前属于第几阶段

final int getPhase()

// 获取当前注册的任务数

int getRegisteredParties()

// 获取当前未到达的任务数

int getUnarrivedParties()

// 判断当前阶段器是否被终止

boolean isTerminated()

Doug Lea 大神



Doug Lea 大神真的很喜欢用一个整数的不同位来表示各种状态,事物数量等。然后各种位运算骚操作,不得不佩服。例如 Phaser 类中只使用一个 long 类型的变量就表示了未到达屏障的任务数,注册的任务数,阶段数,以及阶段器的终止状态等。如果我们来表示这些信息,可能就是定义4个变量了吧!

/**

  • unarrived – the number of parties yet to hit barrier (bits 0-15)

  • parties – the number of parties to wait (bits 16-31)

  • phase – the generation of the barrier (bits 32-62)

  • terminated – set if barrier is terminated (bit 63 / sign)

1200页Java架构面试专题及答案

小编整理不易,对这份1200页Java架构面试专题及答案感兴趣劳烦帮忙转发/点赞

百度、字节、美团等大厂常见面试题

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

用一个 long 类型的变量就表示了未到达屏障的任务数,注册的任务数,阶段数,以及阶段器的终止状态等。如果我们来表示这些信息,可能就是定义4个变量了吧!

/**

  • unarrived – the number of parties yet to hit barrier (bits 0-15)

  • parties – the number of parties to wait (bits 16-31)

  • phase – the generation of the barrier (bits 32-62)

  • terminated – set if barrier is terminated (bit 63 / sign)

1200页Java架构面试专题及答案

小编整理不易,对这份1200页Java架构面试专题及答案感兴趣劳烦帮忙转发/点赞

[外链图片转存中…(img-sPR4Ihg3-1715268476783)]

[外链图片转存中…(img-3nvdG98S-1715268476784)]

百度、字节、美团等大厂常见面试题

[外链图片转存中…(img-UgMX5XnM-1715268476784)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值