【同步工具类:Phaser】

news2025/1/23 6:22:40

同步工具类:Phaser

  • 介绍
    • 特性
      • 动态调整线程个数
      • 层次Phaser
  • 源码分析
    • state 变量解析
    • 构造函数对state变量赋值
    • 阻塞方法
    • arrive()
    • awaitAdvance()
  • 业务场景
    • 实现CountDownLatch功能
      • 代码
      • 测试结果
    • 实现 CyclicBarrier功能
      • 代码展示
      • 测试结果
  • 总结

介绍

一个可重复使用的同步屏障,功能类似于CyclicBarrier和CountDownLatch,但支持更灵活的使用。该工具类是 JDK 1.7才引入的。功能比 CyclicBarrier 和 CountDownLatch 都要强大。
CyclicBarrier 和 CountDownLatch 的学习地址

特性

动态调整线程个数

CyclicBarrier 所要同步的线程个数是在构造函数中指定的,之后不能更改,而Phaser可以在运行期间动态地调整要同步的线程个数。用来修改同步线程个数的函数有:

 public int register() {
        //注册一个线程
        return doRegister(1);
    }
 public int bulkRegister(int parties) {
        if (parties < 0)
            throw new IllegalArgumentException();
        if (parties == 0)
            return getPhase();
            //注册多个线程
        return doRegister(parties);
    }
 public int arriveAndDeregister() {
 		//解注册,减少线程个数
        return doArrive(ONE_DEREGISTER);
    }

层次Phaser

即 多个Phaser 可以组成 如下的树状结构,可以通过在构造函数中传入父Phaser 来实现
在这里插入图片描述

 public Phaser(Phaser parent) {
     //传入Phaser 作为 该对象的父 节点
        this(parent, 0);
    }

如上图,如果传Phaser 参数过来,对应的线程数量会相加。对于树状Phaser 上的每个节点来说,可以当作一个独立的Phaser来看待,其运作机制和一个单独的Phaser 是一样的,具体来讲,Phaser 并不用感知子Phaser的存在,当子Phaser中注册的参与者数量大于0时,会把自己向父节点注册,当子Phaser中注册的参与者数量等于0时,会自动向父节点解除注册。父Phaser把子Phaser当作一个正常参与的线程即可。

源码分析

state 变量解析

Phaser 没有基于AQS来实现,但具备AQS的核心特性:state 变量CAS 操作阻塞队列等。
首先看下state 变量。

    /**
     * Primary state representation, holding four bit-fields:
     *  state 分为了四个部分。
     * 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)
     *
     * Except that a phaser with no registered parties is
     * distinguished by the otherwise illegal state of having zero
     * parties and one unarrived parties (encoded as EMPTY below).
     *
     * To efficiently maintain atomicity, these values are packed into
     * a single (atomic) long. Good performance relies on keeping
     * state decoding and encoding simple, and keeping race windows
     * short.
     *
     * All state updates are performed via CAS except initial
     * registration of a sub-phaser (i.e., one with a non-null
     * parent).  In this (relatively rare) case, we use built-in
     * synchronization to lock while first registering with its
     * parent.
     *
     * The phase of a subphaser is allowed to lag that of its
     * ancestors until it is actually accessed -- see method
     * reconcileState.
     */
    private volatile long state;

从英文注释中我们可以得知:这个64位的state变量被拆分成4部分。如图所示

 ffewaeseafwefafef最高位0表示未同步完成,1表示同步完成。初始最高位为0.可以看下如下几个函数对state 的各个部分组成的获取。获取当前轮数(32-62位)

    /**
     * Returns the current phase number. The maximum phase number is
     * {@code Integer.MAX_VALUE}, after which it restarts at
     * zero. Upon termination, the phase number is negative,
     * in which case the prevailing phase prior to termination
     * may be obtained via {@code getPhase() + Integer.MIN_VALUE}.
     *
     * @return the phase number, or a negative value if terminated
     */
    public final int getPhase() {
       //无符号右移(高位补0) 32位。
       //然后强制int .则最高位还是 是否为同步信息。
       //如果是负数,说明最高位是1,已经完成了同步
        return (int)(root.state >>> PHASE_SHIFT);
    }
    private static final int  PHASE_SHIFT     = 32;

判断当前轮数同步是否完成。

    /**
     * Returns {@code true} if this phaser has been terminated.
     *
     * @return {@code true} if this phaser has been terminated
     */
    public boolean isTerminated() {
       //判断整个值是否是负数即可。最高位也是符号位。
        return root.state < 0L;
    }

获取总注册线程数

    /**
     * Returns the number of parties registered at this phaser.
     *
     * @return the number of parties
     */
    public int getRegisteredParties() {
        return partiesOf(state);
    }
    
    private static int partiesOf(long s) {
       //先将s 转成int.相当于把高位 是否完成同步和轮数舍弃了。
       //只剩下低位的总线程数和未达到线程数。然后向右无符号右移16位(高位补0)。
       //就只剩下了总线程数
        return (int)s >>> PARTIES_SHIFT;
    }
    
    private static final int  PARTIES_SHIFT   = 16;

获取未达到的线程数

    /**
     * Returns the number of registered parties that have not yet
     * arrived at the current phase of this phaser. If this phaser has
     * terminated, the returned value is meaningless and arbitrary.
     *
     * @return the number of unarrived parties
     */
    public int getUnarrivedParties() {
        return unarrivedOf(reconcileState());
    }
    private static int unarrivedOf(long s) {
       // 首先将state强转为int,舍弃高位的 是否完成同步和轮数信息
        int counts = (int)s;
        //不为空的话,则和 0xffff 进行与操作,截取 低16位。即未到达的线程数
        return (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK);
    }
    
     private static final int  EMPTY           = 1;
     private static final int  UNARRIVED_MASK  = 0xffff;      // to mask ints

构造函数对state变量赋值

如果能理解state 变量的分布图。64位的划分,划分为4个部分。 那么就很容易理解构造函数的初始了。

    /**
     * Creates a new phaser with the given parent and number of
     * registered unarrived parties.  When the given parent is non-null
     * and the given number of parties is greater than zero, this
     * child phaser is registered with its parent.
     *
     * @param parent the parent phaser
     * @param parties the number of parties required to advance to the
     * next phase
     * @throws IllegalArgumentException if parties less than zero
     * or greater than the maximum number of parties supported
     */
    public Phaser(Phaser parent, int parties) {
        //初始化未到达的线程数,不能大于2 的16次方。上面有个state 变量划分图
        if (parties >>> PARTIES_SHIFT != 0)
            throw new IllegalArgumentException("Illegal number of parties");
        //初始轮数为0
        int phase = 0;
        this.parent = parent;
        //判断parent 是否为null。不为null.则把自己注册到父对象中
        if (parent != null) {
            final Phaser root = parent.root;
            this.root = root;
            this.evenQ = root.evenQ;
            this.oddQ = root.oddQ;
            if (parties != 0)
                phase = parent.doRegister(1);
        }
        else {
            this.root = this;
            this.evenQ = new AtomicReference<QNode>();
            this.oddQ = new AtomicReference<QNode>();
        }
        //如果parties为0,则赋值EMPTY.
        //不为0,则将 phare 左移 32位  轮数赋值
        //      将parties 左移16位   总线程数数,初始值和未达到数一样
        //       parties            未达到线程数
        // 最后将三个数 进行或运算 (只有有一个是1,则结果为1)
        this.state = (parties == 0) ? (long)EMPTY :
            ((long)phase << PHASE_SHIFT) |
            ((long)parties << PARTIES_SHIFT) |
            ((long)parties);
    }

        private static final int  PARTIES_SHIFT   = 16;
        private static final int  PHASE_SHIFT     = 32;
        private static final int  EMPTY           = 1;

阻塞方法

如下图所示,右边的主线程会调用awaitAdvance()进行阻塞。左边的arrive()会对state进行cas的累减操作。当未到达的线程数减到0时,唤醒右边阻塞的主线程。
在这里插入图片描述
在这里,阻塞使用的是一个称为Treiber Stack的数据结构,而不是AQS的双向链表。Treiber Stack是一个单向链表,出栈和入栈都在链表头部,所以只需要一个head指针,而不需要tail指针。
Treiber Stack 代码

static final class QNode implements ForkJoinPool.ManagedBlocker {
        final Phaser phaser;
        final int phase;
        final boolean interruptible;
        final boolean timed;
        boolean wasInterrupted;
        long nanos;
        final long deadline;
        volatile Thread thread; // nulled to cancel wait 每个Node 对应一个Thread 对象
        QNode next; //单项链表,

        QNode(Phaser phaser, int phase, boolean interruptible,
              boolean timed, long nanos) {
            this.phaser = phaser;
            this.phase = phase;
            this.interruptible = interruptible;
            this.nanos = nanos;
            this.timed = timed;
            this.deadline = timed ? System.nanoTime() + nanos : 0L;
            thread = Thread.currentThread();
        }
}
    /**
     * Heads of Treiber stacks for waiting threads. To eliminate
     * contention when releasing some threads while adding others, we
     * use two of them, alternating across even and odd phases.
     * Subphasers share queues with root to speed up releases.
     */
     //为了减少并发冲突,这里定义了2个链表,也就是2个Treiber Stack。
    private final AtomicReference<QNode> evenQ;
    private final AtomicReference<QNode> oddQ;

    private AtomicReference<QNode> queueFor(int phase) {
        //当phase 为奇数的时候,阻塞线程放在addQ里面
        //        为偶数的时候,阻塞线程放在eventQ里面
        return ((phase & 1) == 0) ? evenQ : oddQ;
    }

arrive()

对state 变量进行操作,然后唤醒线程。
arrive()和arriveAndDeregister()内部调用的都是doArrive() 函数。区别在于前者只是把未达到线程数减一。后者则把未到达线程数和下一轮的总线程数都减一。

   
    public int arrive() {
        return doArrive(ONE_ARRIVAL);
    }
 
    private static final int  ONE_ARRIVAL     = 1;
   
    public int arriveAndDeregister() {
        return doArrive(ONE_DEREGISTER);
    }
    //65536
    private static final int  ONE_PARTY       = 1 << PARTIES_SHIFT;
    //65537
    private static final int  ONE_DEREGISTER  = ONE_ARRIVAL|ONE_PARTY;

doArrive()
把未到达线程数减一,减一之和,还未到1(空置设置的是1,这里和jdk1.7有点区别)。什么都不做。直接返回。如果到1后。说明所有线程到达。 然后会处理两件事:
1)重置state,把state 的未到达线程个数重置到总的注册的线程数中。同时phaser加1
2).唤醒队列中的线程

 private int doArrive(int adjust) {
        final Phaser root = this.root;
        for (;;) {
            long s = (root == this) ? state : reconcileState();
            int phase = (int)(s >>> PHASE_SHIFT);
            //小于0,直接返回
            if (phase < 0)
                return phase;
            int counts = (int)s;
            int unarrived = (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK);
            if (unarrived <= 0)
                throw new IllegalStateException(badArrive(s));
            if (UNSAFE.compareAndSwapLong(this, stateOffset, s, s-=adjust)) {//CAS 减一
                if (unarrived == 1) {//所有线程到达
                    long n = s & PARTIES_MASK;  // base of next state
                    int nextUnarrived = (int)n >>> PARTIES_SHIFT;
                    if (root == this) {//父 Phaser 为空,调用自己的 onAdvance()
                        if (onAdvance(phase, nextUnarrived))
                            n |= TERMINATION_BIT;//最高位置为1
                        else if (nextUnarrived == 0)
                            n |= EMPTY;
                        else
                            n |= nextUnarrived;//下一轮的未到达数等于总的线程数
                        int nextPhase = (phase + 1) & MAX_PHASE;
                        n |= (long)nextPhase << PHASE_SHIFT;
                        //重置state
                        UNSAFE.compareAndSwapLong(this, stateOffset, s, n);
                        //释放所有线程
                        releaseWaiters(phase);
                    }
                    else if (nextUnarrived == 0) { // propagate deregistration
                        //父 Phaser 不为空,调用父 的doArrive()并且下个未到达==0.
                        //则把未到的线程和下一轮未到的线程都减一
                        phase = parent.doArrive(ONE_DEREGISTER);
                        UNSAFE.compareAndSwapLong(this, stateOffset,
                                                  s, s | EMPTY);
                    }
                    else
                    // 父 Phaser 不为空,调用父 的doArrive() 减一个
                        phase = parent.doArrive(ONE_ARRIVAL);
                }
                //没有全部到达,直接返回
                return phase;
            }
        }
    }

releaseWaiters()

 private void releaseWaiters(int phase) {
        QNode q;   // first element of queue
        Thread t;  // its thread
        //根据phase是奇数还是偶数来找对应的栈
        AtomicReference<QNode> head = (phase & 1) == 0 ? evenQ : oddQ;
        //遍历整个栈
        while ((q = head.get()) != null &&
        //如果当前节点的phase 不等于当前Phaser的phase,说明该节点不是当前轮的,而是前一轮的。
        //需要被释放并唤醒
               q.phase != (int)(root.state >>> PHASE_SHIFT)) {
            if (head.compareAndSet(q, q.next) &&
                (t = q.thread) != null) {
                q.thread = null;
                LockSupport.unpark(t);
            }
        }
    }

awaitAdvance()

internalAwaitAdvance 方法主要是调用了ForkJoinPool.manageBlock函数。目的是把Node对应的线程阻塞。

 public int awaitAdvance(int phase) {
        final Phaser root = this.root;
        //root==this 表示没有树状结构。只有一个Phaser
        long s = (root == this) ? state : reconcileState();
        int p = (int)(s >>> PHASE_SHIFT);
        if (phase < 0)
        	//phase 已经结束,不用阻塞了,直接返回
            return phase;
        if (p == phase)
        	//阻塞在这一层上面
            return root.internalAwaitAdvance(phase, null);
        return p;
    }

业务场景

实现CountDownLatch功能

前面我们用 CountDownLatch实现了 主线程等待10 个线程预加载数据完成后再执行 加载其他组件功能。
代码可以参考之前的CountDownLatch文章.

代码

public class DoThing extends Thread{
    private final Phaser startPhaser;
    private final Phaser donePhaser;

    public DoThing(Phaser startSignal, Phaser doneSignal) {
        this.startPhaser = startSignal;
        this.donePhaser = doneSignal;
    }

    @Override
    public void run() {
        try {
            //开始阻塞了,等待主线程开启
            System.out.println(Thread.currentThread().getName()+" 开始阻塞等待了...");
            startPhaser.awaitAdvance(startPhaser.getPhase());
            doWork();
            donePhaser.arrive();
        } catch (InterruptedException ex) {
        }
    }

    public void doWork() throws InterruptedException {
        System.out.println(Thread.currentThread().getName() + " 开始干活了,DB 中的数据加载到本地缓存中");
        Thread.sleep(1000);
    }
}
/**
 * @author :echo_黄诗
 * @description:利用Phaser 来实现 CountDownLatch 功能
 * @date :2023/3/3 18:50
 */
public class Run {
    public static void main(String[] args) {
        Phaser startPhaser = new Phaser(1);
        Phaser donePhaser = new Phaser(10);
        for (int i = 0; i < 10; ++i) // create and start threads
            new Thread(new DoThing(startPhaser, donePhaser)).start();
        doSomethingElse();
        //开始加载数据,
        startPhaser.arrive();
        //主线程阻塞,等待数据加载完成
        donePhaser.awaitAdvance(donePhaser.getPhase());
        doSomethingElse();
        System.out.println("数据加载完成,开始启动其他组件,包括dubbo组件");
    }

    public static void doSomethingElse(){
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

测试结果

在这里插入图片描述

实现 CyclicBarrier功能

前面CyclicBarrier章节 我们利用 CyclicBarrier 实现了10个求职者 一起来了后开始笔试,然后一起面试的功能。之前的CyclicBarrier的代码可以参考.

代码展示

工具类

public class Utils {

    /**
     * 模拟在路上方法
     */
    public static void doOnTheWay() {
        doCostTime(2000);
    }

    /**
     * 模拟笔试过程
     */
    public static void doWriteExam() {
        doCostTime(3000);
    }

    /**
     * 模拟面试过程
     */
    public static void doInterview() {

        doCostTime(5000);
    }


    private static void doCostTime(int time) {
        Random random = new Random();
        try {
            //随机休眠时间
            int count = random.nextInt(time);
            // System.out.println(count);
            Thread.sleep(count);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

面试类

public class Interview extends Thread{

    private Phaser phaser;

    public Interview(Phaser phaser) {
        this.phaser = phaser;
    }

    @Override
    public void run() {
        phaser.arrive();
        Utils.doInterview();
        System.out.println(Thread.currentThread().getName() + "  面试完啦.....");
    }
}

笔试类

public class WriteExam extends Thread{

    private Phaser phaser;

    public WriteExam(Phaser phaser) {
        this.phaser = phaser;
    }

    @Override
    public void run() {
        Utils.doWriteExam();
        phaser.arrive();
        System.out.println(Thread.currentThread().getName() + " 笔试做完了....");
    }
}

来公司路上

public class OnTheWay extends Thread{
    private Phaser phaser;

    public OnTheWay(Phaser phaser) {
        this.phaser = phaser;
    }

    @Override
    public void run() {
        Utils.doOnTheWay();
        System.out.println(Thread.currentThread().getName() + " 已经来公司了...");
        phaser.arrive();
    }
}

运行类
此处需要注意,因为是主线程执行 awaitAdvance。所以需要先执行子线程。不然主线程执行就直接阻塞了。
子线程都没有机会执行。因为子线程是靠主线程启动的。

/**
 * @author :echo_黄诗
 * @description:用Phaser 实现CyclicBarrier功能
 * @date :2023/3/3 19:08
 */
public class Run {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor= new ThreadPoolExecutor(10,
                20,100, TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(100));
        Phaser phaser=new Phaser(10);

        for (int i=0;i<10;i++){
            threadPoolExecutor.execute(new OnTheWay(phaser));
        }
        phaser.awaitAdvance(phaser.getPhase());

        for (int i=0;i<10;i++){
            threadPoolExecutor.execute(new WriteExam(phaser));
        }

        phaser.awaitAdvance(phaser.getPhase());
        for (int i=0;i<10;i++){
            threadPoolExecutor.execute(new Interview(phaser));
        }
        phaser.awaitAdvance(phaser.getPhase());
    }
}

测试结果

从打印截图可以看出,使用Phaser 能很好的实现了CyclicBarrier的同步阻塞功能。
但是目前还不能实现其回调函数的功能。
在这里插入图片描述

总结

Phaser 相比CountDownLatch ,Semaphore,CyclicBarrier 的源码,实现上还是复杂的多。但使用上面比较简单。这里只是给大家一个例子。知道该如何用。目前分析源码也不是很透彻。如果要彻底弄清楚,可以参考 Java并发实现原理这本教程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/386609.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

26- AlexNet和VGG模型分析 (TensorFlow系列) (深度学习)

知识要点 AlexNet 是2012年ISLVRC 2012竞赛的冠军网络。 VGG 在2014年由牛津大学著名研究组 VGG 提出。 一 AlexNet详解 1.1 Alexnet简介 AlexNet 是2012年ISLVRC 2012&#xff08;ImageNet Large Scale Visual Recognition Challenge&#xff09;竞赛的冠军网络&#xff0…

paddle推理部署(cpu)

我没按照官方文档去做&#xff0c;吐槽一下&#xff0c;官方文档有点混乱。。一、概述总结起来&#xff0c;就是用c示例代码&#xff0c;用一个模型做推理。二、示例代码下载https://www.paddlepaddle.org.cn/paddle/paddleinferencehttps://github.com/PaddlePaddle/Paddle-In…

Clion连接Docker,使用HElib库

文章目录需求Clion连接服务器内的DockerDockerCLionDocker内配置HElib库参考需求 HElib库是用C编写的同态加密开源库&#xff0c;一般在Linux下使用为了不混淆生产环境&#xff0c;使用Docker搭建HElib运行环境本地在Windows下开发&#xff0c;使用的IDE为Clion&#xff0c;本…

动态规划:leetcode 121. 买卖股票的最佳时机、122. 买卖股票的最佳时机II

leetcode 121. 买卖股票的最佳时机leetcode 122.买卖股票的最佳时机IIleetcode 121. 买卖股票的最佳时机给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。你只能选择 某一天 买入这只股票&#xff0c;并选择在 未来的某一个不同的日…

node版本管理工具nvm

1.标题卸载nvm和node.js 系统变量中删除nvm添加变量&#xff1a;NVM_HOME和NVM_SYMLINK环境变量中 path&#xff1a;删除nvm自动添加的变量 Path %NVM_HOME%;%NVM_SYMLINK%删除自身安装node环境&#xff0c;参考图一图二 图一 图二 2.安装nvm nvm-window下载------https:/…

ES window 系统环境下连接问题

环境问题&#xff1a;&#xff08;我采用的版本是 elasticsearch-7.9.3&#xff09;注意 开始修正之前的配置&#xff1a;前提&#xff1a;elasticsearch.yml增加或者修正一下配置&#xff1a;xpack.security.enabled: truexpack.license.self_generated.type: basicxpack.secu…

对象实例化【JVM】

JVM对象实例化简介/背景一、创建对象的方式1. new2. Class对象的newInstance方法3. Construstor对象的newInstance(xx)方法4. 使用clone方法二、创建对象的步骤1. 判断对象是否已经加载、链接、初始化2. 为对象分配内存3. 处理并发安全问题4. 初始化分配到的空间5. 设置对象的对…

Tech Lead如何引导团队成员解决问题?

作为一个开发团队的Tech Lead&#xff0c;当团队成员向你寻求帮助时&#xff0c;你有没有说过下面这些话&#xff1f; 你别管了&#xff0c;我来解决这个问题你只要。。。就行了你先做其他的吧&#xff0c;我研究一下&#xff0c;然后告诉你怎么做 当我们说这些话时&#xff…

腾讯免费企业邮箱迁移记录

本文记录在重新申请腾讯企业邮箱的过程。 背景 很多年前&#xff0c;将域名latelee.org 迁移到了阿里云&#xff0c;当时因政策原因无法实名&#xff0c;但能使用。去年3月&#xff0c;阿里云提示无法续费&#xff0c;紧急将其转到外面某服务&#xff0c;继续使用&#xff0c;…

IP地址的工作原理

如果您想了解特定设备为何未按预期方式进行连接&#xff0c;或者想要排查网络无法正常工作的可能原因&#xff0c;它可以帮助您了解 IP 地址的工作原理。互联网协议的工作原理与任何其他语言相同&#xff0c;即使用设定的准则进行通信以传递信息。所有设备都使用此协议与其他连…

jq获取同级或者下级的dom节点的操作

1.使用find找到对应的class或者其他 var class_dom1 obj.find(.class名称);或者 find(span .class名称)2.使用添加背景颜色来确定当前的查找位置 class_dom1.css(background,red);3.通过parent来找到它的上级的dom节点 var parent_li_dom1 class_dom1.parent(li.parent_li…

进阶指针——(2)

本次讲解重点&#xff1a; 6. 函数指针数组 7. 指向函数指针数组的指针 8. 回调函数 在前面我们已经讲解了进阶指针的一部分&#xff0c;我们回顾一下在进阶指针(1)我们学过的难点知识点&#xff1a; int my_strlen(const char* str) {return 0; }int main() {//指针数…

创宇盾重保经验分享,看政府、央企如何防护?

三月重保已经迫近&#xff0c;留给我们的准备时间越来越少&#xff0c;综合近两年三月重保经验及数据总结&#xff0c;知道创宇用实际案例的防护效果说话&#xff0c;深入解析为何创宇盾可以在历次重保中保持“零事故”成绩&#xff0c;受到众多部委、政府、央企/国企客户的青睐…

HACKTHEBOX——Irked

nmapnmap -sV -sC -Pn -T4 -oA nmap 10.10.10.117可能是因为网络原因&#xff0c;与目标链接并不稳定&#xff0c;因此添加了参数-Pn&#xff0c;也只扫描了常见的端口扫描可以看到只开启了3个端口&#xff0c;22,80和111。但是在访问web时&#xff0c;页面提示运行着irc因此再…

WebRTC新增FFmpeg视频编解码模块

1、整体描述目前webrtc内置的视频编解码器包括&#xff1a;VP8、VP9、AV1和H264。一般情况下载pc端基本可以满足大部分的需求&#xff0c;但是有时候为了进行编解码器的扩展包括支持H265或者是支持硬件编解码以提升效率时需要新增编解码模块。2、新增外部编码器编码器实现的要点…

亿万级海量数据去重软方法

文章目录原理案例一需求&#xff1a;方法案例二需求&#xff1a;方法&#xff1a;参考原理 在大数据分布式计算框架生态下&#xff0c;提升计算效率的方法是尽可能的把计算分布式话、并行化&#xff0c;避免单节点计算过载&#xff0c;把计算分摊到各个节点。这样解释小白能够…

最新|移动机器人导航定位技术概述

前言目前工业界广泛落地使用的移动机器人&#xff0c;除了应用场景在餐厅、酒店、超市等小范围室内送餐机器人和消毒机器人外&#xff0c;另外一个“大赛道”应用场景就是在工厂、制造装配车间、电站或车站的物流搬运机器人和巡检机器人了。而在最开始&#xff0c;一切都得从AG…

spring cloud gateway (五)

Gateway简介 Spring Cloud Gateway是Spring公司基于Spring 5.0&#xff0c;Spring Boot 2.0 和 Project Reactor 等技术开发的网关&#xff0c;它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。它的目标是替代Netflix Zuul&#xff0c;其不仅提供统一的路由方式…

java 字典

java 字典 数据结构总览 Map Map 描述的是一种映射关系&#xff0c;一个 key 对应一个 value&#xff0c;可以添加&#xff0c;删除&#xff0c;修改和获取 key/value&#xff0c;util 提供了多种 Map HashMap: hash 表实现的 map&#xff0c;插入删除查找性能都是 O(1)&…

MySQL跨服务器数据映射

MySQL跨服务器数据映射环境准备1. 首先是要查看数据库的federated引擎 开启/关闭 状态2. 打开任务管理器&#xff0c;并重启mysql服务3. 再次查看FEDERATED引擎状态&#xff0c;引擎已启动映射实现问题总结在日常的开发中经常进行跨数据库进行查询数据。 同服务器下跨数据库进…