【源码解析】聊聊线程池 实现原理与源码深度解析(一)

一、Java 线程池 实现原理与源码深度解析

架构

在这里插入图片描述
总揽线程池设计,其实可以发现都是符合顶层的接口设计,中间抽象类,最终是实际工作类

使用示例

public class MyRunnable implements Runnable{

    @Override
    public void run() {
        System.out.println("我是要执行的任务,正在处理中");
    }

    public static void main(String[] args) {
        Runnable runnable = new MyRunnable();
        Runnable runnable2 = new MyRunnable();

        ExecutorService threadPool = Executors.newFixedThreadPool(1);
        threadPool.execute(runnable);
        threadPool.execute(runnable2);

        try {
            threadPool.awaitTermination(10, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Executor接口

public interface Executor {

    void execute(Runnable command);
}

只定义了一个接口方法,核心就是处理具体的工作任务。

ExecutorService

public interface ExecutorService extends Executor {

    // 关闭线程池,已提交的任务继续执行,不接受继续提交新任务
    void shutdown();

    // 关闭线程池,尝试停止正在执行的所有任务,不接受继续提交新任务
    // 它和前面的方法相比,加了一个单词“now”,区别在于它会去停止当前正在进行的任务
    List<Runnable> shutdownNow();

    // 线程池是否已关闭
    boolean isShutdown();

    // 如果调用了 shutdown() 或 shutdownNow() 方法后,所有任务结束了,那么返回true
    // 这个方法必须在调用shutdown或shutdownNow方法之后调用才会返回true
    boolean isTerminated();

    // 等待所有任务完成,并设置超时时间
    // 我们这么理解,实际应用中是,先调用 shutdown 或 shutdownNow,
    // 然后再调这个方法等待所有的线程真正地完成,返回值意味着有没有超时
    boolean awaitTermination(long timeout, TimeUnit unit)
            throws InterruptedException;

    // 提交一个 Callable 任务
    <T> Future<T> submit(Callable<T> task);

    // 提交一个 Runnable 任务,第二个参数将会放到 Future 中,作为返回值,
    // 因为 Runnable 的 run 方法本身并不返回任何东西
    <T> Future<T> submit(Runnable task, T result);

    // 提交一个 Runnable 任务
    Future<?> submit(Runnable task);

    ......
}

AbstractExecutorService

其实java中框架的特点都是设计一个顶层接口,然后有基于顶层接口设计相关的抽象基础类,实现一些基础的功能。AbstractExecutorService最核心的方法就是newTaskFor() 将任务包装成一个FutureTask,FutureTask间接实现了Runnable类。但是最核心的方法,execute AbstractExecutorService是没有实现的。而是需要子类进行实现。

public abstract class AbstractExecutorService implements ExecutorService {

    /**
     * 将任务包装成FutureTask任务。
     */
    protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
        return new FutureTask<T>(runnable, value);
    }

    
    protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
        return new FutureTask<T>(callable);
    }

   
    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        //1.将任务包装成RunableFuture对象,由于RunnableFuture是实现Runable类,所以execute的参数是一个可拓展的类型
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        //2,交给具体的执行器进行实现
        execute(ftask);
        return ftask;
    }

   
    public <T> Future<T> submit(Runnable task, T result) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task, result);
        execute(ftask);
        return ftask;
    }

    
    public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }
}

ThreadPoolExecutor

我们知道通过Executors.newXXX 可以创建固定线程池、可缓存线程池等,但是最终指向的都是ThreadPoolExecutor类

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        //参数异常值处理,直接返回异常
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        //核心线程数
        this.corePoolSize = corePoolSize;
        //最大线程数
        //默认使用核心线程,超过直接,将任务存储到任务队列中,如果任务队列中也满了,查看是否
        //超过最大线程数,如果没有超过就new一个线程执行任务。
        //异常情况,
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        //线程工厂,一般推荐自定义的
        this.threadFactory = threadFactory;
        //拒绝策略
        this.handler = handler;
    }
  • corePoolSize

线程池中的核心线程数。

  • maximumPoolSize

最大线程数,线程池允许创建的最大线程数。如果当前阻塞队列满了,且继续提交任务,则创建新的线程执行任务,前提是当前线程数小于maximumPoolSize;当阻塞队列是无界队列, 则maximumPoolSize则不起作用, 因为无法提交至核心线程池的线程会一直持续地放入workQueue。

无界队列会一直存储任务,所以不会创建新的线程执行任务。

  • workQueue

用来保存等待被执行的任务的阻塞队列. 在JDK中提供了如下阻塞队列:

(1) ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO排序任务;
    (2) LinkedBlockingQuene:基于链表结构的阻塞队列,按FIFO排序任务,吞吐量通常要高于ArrayBlockingQuene;
    (3) SynchronousQuene:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene;
    (4) priorityBlockingQuene:具有优先级的无界阻塞队列;

  • keepAliveTime

空闲线程的保活时间,如果某线程的空闲时间超过这个值都没有任务给它做,那么可以被关闭了。注意这个值并不会对所有线程起作用,如果线程池中的线程数少于等于核心线程数 corePoolSize,那么这些线程不会因为空闲太长时间而被关闭,当然,也可以通过调用 allowCoreThreadTimeOut(true)使核心线程数内的线程也可以被回收;默认情况下,该参数只在线程数大于corePoolSize时才有用, 超过这个时间的空闲线程将被终止。

  • unit

keepAliveTime的单位

  • threadFactory

用于生成线程,一般我们可以用默认的就可以了。通常,我们可以通过它将我们的线程的名字设置得比较可读一些,如 Message-Thread-1, Message-Thread-2 类似这样。

  • handler

线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略:

AbortPolicy:直接抛出异常,默认策略;
      CallerRunsPolicy:用调用者所在的线程来执行任务;
      DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
      DiscardPolicy:直接丢弃任务;
    当然也可以根据应用场景实现RejectedExecutionHandler接口,自定义饱和策略,如记录日志或持久化存储不能处理的任务。

拒绝策略

    public static class AbortPolicy implements RejectedExecutionHandler {
        /**
         * Creates an {@code AbortPolicy}.
         */
        public AbortPolicy() { }

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException("Task " + r.toString() +
                                                 " rejected from " +
                                                 e.toString());
        }
    }
    
    
     public static class DiscardPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code DiscardPolicy}.
         */
        public DiscardPolicy() { }

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        }
    }
    
    
    public static class DiscardOldestPolicy implements RejectedExecutionHandler {

        public DiscardOldestPolicy() { }


        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                e.getQueue().poll();
                e.execute(r);
            }
        }
    }
    
    public static class CallerRunsPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code CallerRunsPolicy}.
         */
        public CallerRunsPolicy() { }
      
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                r.run();
            }
        }
    }

属性

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

// 这里 COUNT_BITS 设置为 29(32-3),意味着前三位用于存放线程状态,后29位用于存放线程数
private static final int COUNT_BITS = Integer.SIZE - 3;

// 000 11111111111111111111111111111
// 这里得到的是 29 个 1,也就是说线程池的最大线程数是 2^29-1=536870911
// 以我们现在计算机的实际情况,这个数量还是够用的
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

// 我们说了,线程池的状态存放在高 3 位中
// 运算结果为 111跟29个0:111 00000000000000000000000000000
private static final int RUNNING    = -1 << COUNT_BITS;
// 000 00000000000000000000000000000
private static final int SHUTDOWN   =  0 << COUNT_BITS;
// 001 00000000000000000000000000000
private static final int STOP       =  1 << COUNT_BITS;
// 010 00000000000000000000000000000
private static final int TIDYING    =  2 << COUNT_BITS;
// 011 00000000000000000000000000000
private static final int TERMINATED =  3 << COUNT_BITS;

// 将整数 c 的低 29 位修改为 0,就得到了线程池的状态
private static int runStateOf(int c)     { return c & ~CAPACITY; }
// 将整数 c 的高 3 为修改为 0,就得到了线程池中的线程数
private static int workerCountOf(int c)  { return c & CAPACITY; }

private static int ctlOf(int rs, int wc) { return rs | wc; }

private static boolean runStateLessThan(int c, int s) {
    return c < s;
}

private static boolean runStateAtLeast(int c, int s) {
    return c >= s;
}

private static boolean isRunning(int c) {
    return c < SHUTDOWN;
}

属性主要是通过描述线程池的状态,

  • RUNNING:这个没什么好说的,这是最正常的状态:接受新的任务,处理等待队列中的任务
  • SHUTDOWN:不接受新的任务提交,但是会继续处理等待队列中的任务
  • STOP:不接受新的任务提交,不再处理等待队列中的任务,中断正在执行任务的线程
  • TIDYING:所有的任务都销毁了,workCount 为 0。线程池的状态在转换为 TIDYING 状态时,会执行钩子方法 terminated()
  • TERMINATED:terminated() 方法结束后,线程池的状态就会变成这个

在这里插入图片描述

Worker

将执行的任务封装成一个Woker,内部通过引用真正执行任务的线程和实际的任务。

    private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
    {

        /** 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;

        /**
         * 将运行任务封装成worker
         * 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);
        }
    }

execute

判断是否创建线程

    public void execute(Runnable command) {
        // 若任务为空,则抛 NPE,不能执行空任务
        if (command == null) {
            throw new NullPointerException();
        }
        // 表示线程数的状态和线程数的整数
        int c = ctl.get();
        // 若工作线程数小于核心线程数,则创建新的线程,并把当前任务 command 作为这个线程的第一个任务
        //一个新创建的线程池,里面没有线程
        if (workerCountOf(c) < corePoolSize) {
            //如果当前线程小于核心线程数,则创建核心线程数
            if (addWorker(command, true)) {
                return;
            }
            //添加核心线程数失败了,重新获取。可能存在同时多个线程任务创建任务,A,B,C
            c = ctl.get();
        }
        /**
         * 至此,有以下两种情况:
         * 1.当前工作线程数大于等于核心线程数 
         * 2.新建核心线程失败
         * 此时会尝试将任务添加到阻塞队列 workQueue
         */
        // 若线程池处于 RUNNING 状态,将任务添加到阻塞队列 workQueue 中
        if (isRunning(c) && workQueue.offer(command)) {
            // 再次检查线程池标记
            int recheck = ctl.get();
            // 如果线程池已不处于 RUNNING 状态,那么移除已入队的任务,并且执行拒绝策略
            if (!isRunning(recheck) && remove(command)) {
                // 任务添加到阻塞队列失败,执行拒绝策略
                reject(command);
            }
            // 如果线程池还是 RUNNING 的,并且线程数为 0,那么开启新的线程
            else if (workerCountOf(recheck) == 0) {
                addWorker(null, false);
            }
        }
        /**
         * 至此,有以下两种情况:
         * 1.线程池处于非运行状态,线程池不再接受新的线程
         * 2.线程处于运行状态,但是阻塞队列已满,无法加入到阻塞队列
         * 此时会尝试以最大线程数为限制创建新的工作线程
         */
        else if (!addWorker(command, false)) {
            // 任务进入线程池失败,执行拒绝策略
            reject(command);
        }
    }

执行任务是通过threadPool.execute(runnable); 进行执行的,这里其实就是核心的流程。

addWorker

主要的作用就是将要执行的任务添加到set中

    //第一个参数是要执行的任务,第二个参数是是否是核心线程
    private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        //经过判断处理,给工作线程数+1
        for (;;) {
            int c = ctl.get(); //线程个数
            int rs = runStateOf(c);//线程池运行状态

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN &&
                    !(rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty())) {
                //线程池状态stop或者其他状态
                return false;
            }

            for (;;) {
                int wc = workerCountOf(c);
                //1.目前线程个数超过总容量
                //2.大于核心线程或者大于最大线程数 返回true
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                //使用CAS 添加线程数
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                //重新获取状态
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }

        //
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            //将任务封装成一个Worker
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                //加锁 获取线程池的全局锁,避免添加任务时,避免线程之间竞争,
                //避免中断当前执行的任务。
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    int rs = runStateOf(ctl.get());

                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        //将工作线程保存在集合中 使用hashSet保存
                        workers.add(w);
                        int s = workers.size();
                        if (s > largestPoolSize)
                            //记录最大值
                            largestPoolSize = s;
                        workerAdded = true; //添加工作线程成功
                    }
                } finally {
                    mainLock.unlock();
                }
                // 如果成功添加了 Worker,就可以启动 Worker 了
                if (workerAdded) {
                    t.start(); //启动工作线程
                    workerStarted = true; //启动工作线程成功
                }
            }
        } finally {
            if (! workerStarted) //启动失败
                addWorkerFailed(w);
        }
        return workerStarted;
    }

Worker

    private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
    {
        /**
         * This class will never be serialized, but we provide a
         * serialVersionUID to suppress a javac warning.
         */
        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;

        /**
         * 将运行任务封装成worker
         * 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);
        }

        //任务实际执行的方法
        //因为上层是通过firstTask将要执行的任务传递进来的,所以具体执行就是run 
        /** Delegates main run loop to outer runWorker  */
        public void run() {
            runWorker(this);
        }
 }

runWorker

    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            //执行任务的地方 如果获取不到任务就阻塞
            //getTask()方法
            while (task != null || (task = getTask()) != null) {
                w.lock(); //加锁
                //判断当前线程是否大于stop 被拒
                //
                if ((runStateAtLeast(ctl.get(), STOP) || (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) && !wt.isInterrupted()) {
                    wt.interrupt();
                }
                try {
                    //执行任务前的操作
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        //这里其实就是执行Worker的run方法
                        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 {
            //getTask返回了null,非核心的指定时间没有获取到任务超过设置的时候,woker结束
            processWorkerExit(w, completedAbruptly);
        }
    }

getTask

    //1,核心线程不会销毁,所以一直会等待获取任务
    //2.非核心超过了设置的等待时间
    private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?

        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }

            int wc = workerCountOf(c);

            // Are workers subject to culling?
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            try {
                Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

小结

在这里插入图片描述

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深度学习——第3章 Python程序设计语言(3.2 Python程序流程控制)

3.2 Python程序流程控制 目录 1.布尔数据类型及相关运算 2.顺序结构 3.选择&#xff08;分支&#xff09;结构 4.循环结构 无论是在机器学习还是深度学习中&#xff0c;Python已经成为主导性的编程语言。而且&#xff0c;现在许多主流的深度学习框架&#xff0c;例如PyTorc…

EXPLAIN解析

针对以下sql进行解析 EXPLAIN SELECTdauk.id AS daukId,dasm.mailbox AS storeAccount,dau.id,dau.id AS userId,das.score AS score,das.sell_num AS sellNum,dapa.product_link AS productLink,dapa.able_category_ids AS ableCategoryIds,dac.parent_name AS parentName,da…

SAP_ABAP_RZ11解决SAP运行超时问题 TIME_OUT / rdisp/scheduler/prio_high/max_runtime

SAP ABAP 顾问&#xff08;开发工程师&#xff09;能力模型_Terry谈企业数字化的博客-CSDN博客文章浏览阅读510次。目标&#xff1a;基于对SAP abap 顾问能力模型的梳理&#xff0c;给一年左右经验的abaper 快速成长为三年经验提供超级燃料&#xff01;https://blog.csdn.net/j…

Unity | 渡鸦避难所-1 | 修复资源导入后呈现洋红色(Built-in 转 URP)

1 前言 Unity 编辑器导入 Asset Store 的资源包后&#xff0c;在预览和使用时&#xff0c;发现对象显示为洋红色 以小狐狸为例&#xff0c;打开资源包中的场景&#xff0c;可以看到小狐狸和地板均显示为洋红色 这是因为 Asset Store 中的资源包大部分是针对内置渲染管线项目制…

自然语言处理:电脑如何理解我们的语言?

☁️主页 Nowl &#x1f525;专栏《机器学习实战》 《机器学习》 &#x1f4d1;君子坐而论道&#xff0c;少年起而行之 ​ 文章目录 ​编辑 常见方法 1.基于词典的方法 2.基于计数的方法 基于推理的方法 Bert input_ids attention_mask token_type_ids 结语 在广…

施密特触发器

1、作用 简单来说&#xff0c;施密特触发器可以将模拟信号转变成数字信号 2、为什么不使用比较器将模拟信号转变成数字信号 当输入电平高于参考电压时&#xff0c;输出高电平&#xff1b;输入电压低于参考电压时&#xff0c;输出低电平。这样比较器也可以实现模拟信号转换成数…

封装Servlet使用自定义注解进行参数接收

文章目录 前言一、前后对比✨二、具体实现&#x1f387;三、效果展示&#x1f38f; 前言 先说项目背景&#xff0c;本项目是本人在校期间老师布置的作业&#xff08;就一个CRUD&#xff09;&#xff0c;课程是后端应用程序设计&#xff0c;其实就是servlet和jsp那一套&#xf…

Python异常处理【侯小啾python领航班系列(二十六)】

Python异常处理【侯小啾python领航班系列(二十六)】 大家好,我是博主侯小啾, 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹…

Java三种代理模式:静态代理、动态代理和CGLIB代理

Java三种代理模式&#xff1a;静态代理、动态代理和CGLIB代理 代理模式 代理模式是23种设计模式种的一种。代理模式是一种结构型设计模式&#xff0c;它允许为其他对象提供一个替代品或占位符&#xff0c;以控制对这个对象的访问。代理模式可以在不修改被代理对象的基础上&am…

SPM/SCM 流量跟踪体系

SPM SPM&#xff08;shopping page mark&#xff0c;导购页面标记&#xff09; 是淘宝社区电商业务&#xff08;xTao&#xff09;为外部合作伙伴&#xff08;外站&#xff09;提供的跟踪引导成交效果数据的解决方案 注&#xff1a;也有解释 SPM 为超级位置模型(Super Position…

2024年甘肃省职业院校技能大赛(中职教师组)网络安全竞赛样题卷④

2024年甘肃省职业院校技能大赛&#xff08;中职教师组&#xff09;网络安全竞赛样题卷④ 2024年甘肃省职业院校技能大赛&#xff08;中职教师组&#xff09;网络安全竞赛样题卷④A模块基础设施设置/安全加固&#xff08;本模块200分&#xff09;A-1任务一 登录安全加固&#xf…

(C++)和为s的两个数字--双指针算法

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 和为S的两个数字_牛客题霸_牛客网输入一个升序数组 array 和一个数字S&#xff0c;在数组中查找两个数&#xff0c;使得他们的和正好是S&#xff0c;如果。题目来自【牛客题霸】https://www.nowcoder.com/practice/390da4f7a…

关于你对 Zookeeper 的理解

看看普通人和高手是如何回答这个问题的&#xff1f; 普通人 Zookeeper 是一种开放源码的分布式应用程序协调服务 是一个分布式的小文件存储系统 一般对开发者屏蔽分布式应用开发过过程种的底层细节 用来解决分布式集群中应用系统的一致性问题 高手 对于 Zookeeper 的理解…

网工学习9-STP配置

如图 1 所示&#xff0c;当前网络中存在环路&#xff0c; SwitchA 、SwitchB 、SwitchC 和 SwitchD 都运行 STP&#xff0c;通过 彼此交互信息发现网络中的环路&#xff0c;并有选择的对某个端口进行阻塞&#xff0c;最终将环形网络结构修剪成无 环路的树形网络结构&#xff…

python | 使用open读写文件

在目前的环境中&#xff0c;读取文件应该算是最基本的操作了&#xff0c;python也内置了读写文件的函数&#xff0c;让我们来看下。这里有个小点要点一下&#xff0c;我们使用python是没法办直接操作文件的&#xff0c;而是操作系统给我们预留了接口&#xff0c;python通过操作…

关于this和构造器的理解

1.类中的this关键字表示当前对象的引用。它可以被用于解决变量名冲突问题&#xff0c;或者在一个方法中调用类的另一个方法。如果在方法中没有明确指定要使用哪个变量&#xff0c;那么编译器就会默认使用this关键字来表示当前对象。 下面是一个输出this关键字的示例代码&#…

从朋友的反思中想到的一些关于外贸的问题

昨天有两个好消息&#xff0c;一个是上星期客户要开业的项目昨天顺利搭建完成&#xff0c;没有任何东西缺少&#xff0c;虽然指导安装过程中也是一堆糟心的事&#xff0c;还好只是有惊无险&#xff0c;同时也收到来自中间商的感谢信&#xff0c;她说没想到如此复杂的搭建甚至每…

海外的直播平台

1、Netflix Netflix以允许人们从众多设备观看系列和电影而闻名。用户可以开设一个帐户并添加不同的用户个人资料&#xff0c;这对于想要为整个家庭拥有单独帐户而又无需开设大量帐户并单独付款的人来说非常有用。它现在增加了一个直播服务&#xff0c;允许观众参加现场练习课程…

Mars3d标绘的时候通过绑定单击事件,查询点击落点的图层类型

需求期望&#xff1a; 期望可以判断标绘点落下的位置是什么图层类型&#xff0c;例如是否是3dtitles模型&#xff0c;或者是gltf模型&#xff0c;或者是其他数据图层。 需求来源&#xff1a; 标绘点时&#xff0c;无法知道点落下的地方的图层类型 解决方案&#xff1a; ma…

外贸中遇到客户要退款的那些事

最近一个小伙伴说自己的一个客户在付费平台上用信用卡支付的货款&#xff0c;结果客户收到货之后却说自己欺瞒对方&#xff0c;发的货和客户所要的货有差距&#xff0c;要在平台申请退款&#xff0c;然后在群里询问大家有什么好的注意或者方案。 小伙伴说自己的这个产品本身之…