我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Java中的ForkJoin是什么及怎么调用

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Java中的ForkJoin是什么及怎么调用

本篇内容主要讲解“Java中的ForkJoin是什么及怎么调用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java中的ForkJoin是什么及怎么调用”吧!

    什么是ForkJoin?

    ForkJoin 从字面上看Fork是分岔的意思,Join是结合的意思,我们可以理解为将大任务拆分成小任务进行计算求解,最后将小任务的结果进行结合求出大任务的解,这些裂变出来的小任务,我们就可以交给不同的线程去进行计算,这也就是分布式计算的一种思想。这与大数据中的分布式离线计算MapReduce类似,对ForkJoin最经典的一个应用就是Java8中的Stream,我们知道Stream分为串行流和并行流,其中并行流parallelStream就是依赖于ForkJoin来实现并行处理的。

    下面我们一起来看一下最为核心的ForkJoinTaskForkJoinPool

    ForkJoinTask 任务

    ForkJoinTask本身的依赖关系并不复杂,它与异步任务计算FutureTask一样均实现了Future接口

    Java中的ForkJoin是什么及怎么调用

    下面我们就ForkJoinTask的核心源码来研究一下,该任务是如何通过分治法进行计算。

    ForkJoinTask最核心的莫过于fork()和join()方法了。

    fork()

    • 判断当前线程是不是ForkJoinWorkerThread线程

      • 是 直接将当前线程push到工作队列中

      • 否 调用ForkJoinPool 的externalPush方法

    ForkJoinPool构建了一个静态的common对象,这里调用的就是commonexternalPush()

    join()

    • 调用doJoin()方法,等待线程执行完成

        public final ForkJoinTask<V> fork() {        Thread t;        if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)            ((ForkJoinWorkerThread)t).workQueue.push(this);        else            ForkJoinPool.common.externalPush(this);        return this;    }    public final V join() {        int s;        if ((s = doJoin() & DONE_MASK) != NORMAL)            reportException(s);        return getRawResult();    }    private int doJoin() {        int s; Thread t; ForkJoinWorkerThread wt; ForkJoinPool.WorkQueue w;        return (s = status) < 0 ? s :            ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?            (w = (wt = (ForkJoinWorkerThread)t).workQueue).            tryUnpush(this) && (s = doExec()) < 0 ? s :            wt.pool.awaitJoin(w, this, 0L) :            externalAwaitDone();    }// 获取结果的方法由子类实现public abstract V getRawResult();

    RecursiveTask 是ForkJoinTask的一个子类主要对获取结果的方法进行了实现,通过泛型约束结果。我们如果需要自己创建任务,仍需要实现RecursiveTask,并去编写最为核心的计算方法compute()。

    public abstract class RecursiveTask<V> extends ForkJoinTask<V> {    private static final long serialVersionUID = 5232453952276485270L;    V result;    protected abstract V compute();    public final V getRawResult() {        return result;    }    protected final void setRawResult(V value) {        result = value;    }    protected final boolean exec() {        result = compute();        return true;    }}

    ForkJoinPool 线程池

    ForkJoinTask 中许多功能都依赖于ForkJoinPool线程池,所以说ForkJoinTask运行离不开ForkJoinPool,ForkJoinPool与ThreadPoolExecutor有许多相似之处,他是专门用来执行ForkJoinTask任务的线程池,我之前也有文章对线程池技术进行了介绍,感兴趣的可以进行阅读&mdash;&mdash;从java源码分析线程池(池化技术)的实现原理

    ForkJoinPool与ThreadPoolExecutor的继承关系几乎是相同的,他们相当于兄弟关系。

    Java中的ForkJoin是什么及怎么调用

    工作窃取算法

    ForkJoinPool中采取工作窃取算法,如果每次fork子任务如果都去创建新线程去处理的话,对系统资源的开销是巨大的,所以必须采取线程池。一般的线程池只有一个任务队列,但是对于ForkJoinPool来说,由于同一个任务Fork出的各个子任务是平行关系,为了提高效率,减少线程的竞争,需要将这些平行的任务放到不同的队列中,由于线程处理不同任务的速度不同,这样就可能存在某个线程先执行完了自己队列中的任务,这时为了提升效率,就可以让该线程去“窃取”其它任务队列中的任务,这就是所谓的“工作窃取算法”。

    对于一般的队列来说,入队元素都是在队尾,出队元素在队首,要满足“工作窃取”的需求,任务队列应该支持从“队尾”出队元素,这样可以减少与其它工作线程的冲突(因为其它工作线程会从队首获取自己任务队列中的任务),这时就需要使用双端阻塞队列来解决。

    构造方法

    首先我们来看ForkJoinPool线程池的构造方法,他为我们提供了三种形式的构造,其中最为复杂的是四个入参的构造,下面我们看一下它四个入参都代表什么?

    • int parallelism 可并行级别(不代表最多存在的线程数量)

    • ForkJoinWorkerThreadFactory factory 线程创建工厂

    • UncaughtExceptionHandler handler 异常捕获处理器

    • boolean asyncMode 先进先出的工作模式 或者 后进先出的工作模式

        public ForkJoinPool() {        this(Math.min(MAX_CAP, Runtime.getRuntime().availableProcessors()),             defaultForkJoinWorkerThreadFactory, null, false);    }public ForkJoinPool(int parallelism) {        this(parallelism, defaultForkJoinWorkerThreadFactory, null, false);    }public ForkJoinPool(int parallelism,                        ForkJoinWorkerThreadFactory factory,                        UncaughtExceptionHandler handler,                        boolean asyncMode) {        this(checkParallelism(parallelism),             checkFactory(factory),             handler,             asyncMode ? FIFO_QUEUE : LIFO_QUEUE,             "ForkJoinPool-" + nextPoolId() + "-worker-");        checkPermission();    }

    提交方法

    下面我们看一下提交任务的方法:

    externalPush这个方法我们很眼熟,它正是在fork的时候如果当前线程不是ForkJoinWorkerThread,新提交任务也是会通过这个方法去执行任务。由此可见,fork就是新建一个子任务进行提交。

    externalSubmit是最为核心的一个方法,它可以首次向池提交第一个任务,并执行二次初始化。它还可以检测外部线程的首次提交,并创建一个新的共享队列。

    signalWork(ws, q)是发送工作信号,让工作队列进行运转。

        public ForkJoinTask<?> submit(Runnable task) {        if (task == null)            throw new NullPointerException();        ForkJoinTask<?> job;        if (task instanceof ForkJoinTask<?>) // avoid re-wrap            job = (ForkJoinTask<?>) task;        else            job = new ForkJoinTask.AdaptedRunnableAction(task);        externalPush(job);        return job;    }    final void externalPush(ForkJoinTask<?> task) {        WorkQueue[] ws; WorkQueue q; int m;        int r = ThreadLocalRandom.getProbe();        int rs = runState;        if ((ws = workQueues) != null && (m = (ws.length - 1)) >= 0 &&            (q = ws[m & r & SQMASK]) != null && r != 0 && rs > 0 &&            U.compareAndSwapInt(q, QLOCK, 0, 1)) {            ForkJoinTask<?>[] a; int am, n, s;            if ((a = q.array) != null &&                (am = a.length - 1) > (n = (s = q.top) - q.base)) {                int j = ((am & s) << ASHIFT) + ABASE;                U.putOrderedObject(a, j, task);                U.putOrderedInt(q, QTOP, s + 1);                U.putOrderedInt(q, QLOCK, 0);                if (n <= 1)                    signalWork(ws, q);                return;            }            U.compareAndSwapInt(q, QLOCK, 1, 0);        }        externalSubmit(task);    }    private void externalSubmit(ForkJoinTask<?> task) {        int r;                                    // initialize caller's probe        if ((r = ThreadLocalRandom.getProbe()) == 0) {            ThreadLocalRandom.localInit();            r = ThreadLocalRandom.getProbe();        }        for (;;) {            WorkQueue[] ws; WorkQueue q; int rs, m, k;            boolean move = false;            if ((rs = runState) < 0) {                tryTerminate(false, false);     // help terminate                throw new RejectedExecutionException();            }            else if ((rs & STARTED) == 0 ||     // initialize                     ((ws = workQueues) == null || (m = ws.length - 1) < 0)) {                int ns = 0;                rs = lockRunState();                try {                    if ((rs & STARTED) == 0) {                        U.compareAndSwapObject(this, STEALCOUNTER, null,                                               new AtomicLong());                        // create workQueues array with size a power of two                        int p = config & SMASK; // ensure at least 2 slots                        int n = (p > 1) ? p - 1 : 1;                        n |= n >>> 1; n |= n >>> 2;  n |= n >>> 4;                        n |= n >>> 8; n |= n >>> 16; n = (n + 1) << 1;                        workQueues = new WorkQueue[n];                        ns = STARTED;                    }                } finally {                    unlockRunState(rs, (rs & ~RSLOCK) | ns);                }            }            else if ((q = ws[k = r & m & SQMASK]) != null) {                if (q.qlock == 0 && U.compareAndSwapInt(q, QLOCK, 0, 1)) {                    ForkJoinTask<?>[] a = q.array;                    int s = q.top;                    boolean submitted = false; // initial submission or resizing                    try {                      // locked version of push                        if ((a != null && a.length > s + 1 - q.base) ||                            (a = q.growArray()) != null) {                            int j = (((a.length - 1) & s) << ASHIFT) + ABASE;                            U.putOrderedObject(a, j, task);                            U.putOrderedInt(q, QTOP, s + 1);                            submitted = true;                        }                    } finally {                        U.compareAndSwapInt(q, QLOCK, 1, 0);                    }                    if (submitted) {                        signalWork(ws, q);                        return;                    }                }                move = true;                   // move on failure            }            else if (((rs = runState) & RSLOCK) == 0) { // create new queue                q = new WorkQueue(this, null);                q.hint = r;                q.config = k | SHARED_QUEUE;                q.scanState = INACTIVE;                rs = lockRunState();           // publish index                if (rs > 0 &&  (ws = workQueues) != null &&                    k < ws.length && ws[k] == null)                    ws[k] = q;                 // else terminated                unlockRunState(rs, rs & ~RSLOCK);            }            else                move = true;                   // move if busy            if (move)                r = ThreadLocalRandom.advanceProbe(r);        }    }

    创建工人(线程)

    提交任务后,通过signalWork(ws, q)方法,发送工作信号,当符合没有执行完毕,且没有出现异常的条件下,循环执行任务,根据控制变量尝试添加工人(线程),通过线程工厂,生成线程,并且启动线程,也控制着工人(线程)的下岗。

        final void signalWork(WorkQueue[] ws, WorkQueue q) {        long c; int sp, i; WorkQueue v; Thread p;        while ((c = ctl) < 0L) {                       // too few active            if ((sp = (int)c) == 0) {                  // no idle workers                if ((c & ADD_WORKER) != 0L)            // too few workers                    tryAddWorker(c);                break;            }            if (ws == null)                            // unstarted/terminated                break;            if (ws.length <= (i = sp & SMASK))         // terminated                break;            if ((v = ws[i]) == null)                   // terminating                break;            int vs = (sp + SS_SEQ) & ~INACTIVE;        // next scanState            int d = sp - v.scanState;                  // screen CAS            long nc = (UC_MASK & (c + AC_UNIT)) | (SP_MASK & v.stackPred);            if (d == 0 && U.compareAndSwapLong(this, CTL, c, nc)) {                v.scanState = vs;                      // activate v                if ((p = v.parker) != null)                    U.unpark(p);                break;            }            if (q != null && q.base == q.top)          // no more work                break;        }    }    private void tryAddWorker(long c) {        boolean add = false;        do {            long nc = ((AC_MASK & (c + AC_UNIT)) |                       (TC_MASK & (c + TC_UNIT)));            if (ctl == c) {                int rs, stop;                 // check if terminating                if ((stop = (rs = lockRunState()) & STOP) == 0)                    add = U.compareAndSwapLong(this, CTL, c, nc);                unlockRunState(rs, rs & ~RSLOCK);                if (stop != 0)                    break;                if (add) {                    createWorker();                    break;                }            }        } while (((c = ctl) & ADD_WORKER) != 0L && (int)c == 0);    }    private boolean createWorker() {        ForkJoinWorkerThreadFactory fac = factory;        Throwable ex = null;        ForkJoinWorkerThread wt = null;        try {            if (fac != null && (wt = fac.newThread(this)) != null) {                wt.start();                return true;            }        } catch (Throwable rex) {            ex = rex;        }        deregisterWorker(wt, ex);        return false;    }   final void deregisterWorker(ForkJoinWorkerThread wt, Throwable ex) {        WorkQueue w = null;        if (wt != null && (w = wt.workQueue) != null) {            WorkQueue[] ws;                           // remove index from array            int idx = w.config & SMASK;            int rs = lockRunState();            if ((ws = workQueues) != null && ws.length > idx && ws[idx] == w)                ws[idx] = null;            unlockRunState(rs, rs & ~RSLOCK);        }        long c;                                       // decrement counts        do {} while (!U.compareAndSwapLong                     (this, CTL, c = ctl, ((AC_MASK & (c - AC_UNIT)) |                                           (TC_MASK & (c - TC_UNIT)) |                                           (SP_MASK & c))));        if (w != null) {            w.qlock = -1;                             // ensure set            w.transferStealCount(this);            w.cancelAll();                            // cancel remaining tasks        }        for (;;) {                                    // possibly replace            WorkQueue[] ws; int m, sp;            if (tryTerminate(false, false) || w == null || w.array == null ||                (runState & STOP) != 0 || (ws = workQueues) == null ||                (m = ws.length - 1) < 0)              // already terminating                break;            if ((sp = (int)(c = ctl)) != 0) {         // wake up replacement                if (tryRelease(c, ws[sp & m], AC_UNIT))                    break;            }            else if (ex != null && (c & ADD_WORKER) != 0L) {                tryAddWorker(c);                      // create replacement                break;            }            else                                      // don't need replacement                break;        }        if (ex == null)                               // help clean on way out            ForkJoinTask.helpExpungeStaleExceptions();        else                                          // rethrow            ForkJoinTask.rethrow(ex);    }    public static interface ForkJoinWorkerThreadFactory {        public ForkJoinWorkerThread newThread(ForkJoinPool pool);    }    static final class DefaultForkJoinWorkerThreadFactory        implements ForkJoinWorkerThreadFactory {        public final ForkJoinWorkerThread newThread(ForkJoinPool pool) {            return new ForkJoinWorkerThread(pool);        }    }    protected ForkJoinWorkerThread(ForkJoinPool pool) {        // Use a placeholder until a useful name can be set in registerWorker        super("aForkJoinWorkerThread");        this.pool = pool;        this.workQueue = pool.registerWorker(this);    }    final WorkQueue registerWorker(ForkJoinWorkerThread wt) {        UncaughtExceptionHandler handler;        wt.setDaemon(true);                           // configure thread        if ((handler = ueh) != null)            wt.setUncaughtExceptionHandler(handler);        WorkQueue w = new WorkQueue(this, wt);        int i = 0;                                    // assign a pool index        int mode = config & MODE_MASK;        int rs = lockRunState();        try {            WorkQueue[] ws; int n;                    // skip if no array            if ((ws = workQueues) != null && (n = ws.length) > 0) {                int s = indexSeed += SEED_INCREMENT;  // unlikely to collide                int m = n - 1;                i = ((s << 1) | 1) & m;               // odd-numbered indices                if (ws[i] != null) {                  // collision                    int probes = 0;                   // step by approx half n                    int step = (n <= 4) ? 2 : ((n >>> 1) & EVENMASK) + 2;                    while (ws[i = (i + step) & m] != null) {                        if (++probes >= n) {                            workQueues = ws = Arrays.copyOf(ws, n <<= 1);                            m = n - 1;                            probes = 0;                        }                    }                }                w.hint = s;                           // use as random seed                w.config = i | mode;                w.scanState = i;                      // publication fence                ws[i] = w;            }        } finally {            unlockRunState(rs, rs & ~RSLOCK);        }        wt.setName(workerNamePrefix.concat(Integer.toString(i >>> 1)));        return w;    }

    例:ForkJoinTask实现归并排序

    这里我们就用经典的归并排序为例,构建一个我们自己的ForkJoinTask,按照归并排序的思路,重写其核心的compute()方法,通过ForkJoinPool.submit(task)提交任务,通过get()同步获取任务执行结果。

    package com.zhj.interview;import java.util.*;import java.util.concurrent.ExecutionException;import java.util.concurrent.ForkJoinPool;import java.util.concurrent.RecursiveTask;public class Test16 {    public static void main(String[] args) throws ExecutionException, InterruptedException {        int[] bigArr = new int[10000000];        for (int i = 0; i < 10000000; i++) {            bigArr[i] = (int) (Math.random() * 10000000);        }        ForkJoinPool forkJoinPool = new ForkJoinPool();        MyForkJoinTask task = new MyForkJoinTask(bigArr);        long start = System.currentTimeMillis();        forkJoinPool.submit(task).get();        long end = System.currentTimeMillis();        System.out.println("耗时:" + (end-start));}}class MyForkJoinTask extends RecursiveTask<int[]> {    private int source[];    public MyForkJoinTask(int source[]) {        if (source == null) {            throw new RuntimeException("参数有误!!!");        }        this.source = source;    }    @Override    protected int[] compute() {        int l = source.length;        if (l < 2) {            return Arrays.copyOf(source, l);        }        if (l == 2) {            if (source[0] > source[1]) {                int[] tar = new int[2];                tar[0] = source[1];                tar[1] = source[0];                return tar;            } else {                return Arrays.copyOf(source, l);            }        }        if (l > 2) {            int mid = l / 2;            MyForkJoinTask task1 = new MyForkJoinTask(Arrays.copyOf(source, mid));            task1.fork();            MyForkJoinTask task2 = new MyForkJoinTask(Arrays.copyOfRange(source, mid, l));            task2.fork();            int[] res1 = task1.join();            int[] res2 = task2.join();            int tar[] = merge(res1, res2);            return tar;        }        return null;    }// 合并数组    private int[] merge(int[] res1, int[] res2) {        int l1 = res1.length;        int l2 = res2.length;        int l = l1 + l2;        int tar[] = new int[l];        for (int i = 0, i1 = 0, i2 = 0; i < l; i++) {            int v1 = i1 >= l1 ? Integer.MAX_VALUE : res1[i1];            int v2 = i2 >= l2 ? Integer.MAX_VALUE : res2[i2];            // 如果条件成立,说明应该取数组array1中的值            if(v1 < v2) {                tar[i] = v1;                i1++;            } else {                tar[i] = v2;                i2++;            }        }        return tar;    }}

    ForkJoin计算流程

    通过ForkJoinPool提交任务,获取结果流程如下,拆分子任务不一定是二分的形式,可参照MapReduce的模式,也可以按照具体需求进行灵活的设计。

    Java中的ForkJoin是什么及怎么调用

    到此,相信大家对“Java中的ForkJoin是什么及怎么调用”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

    免责声明:

    ① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

    ② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

    Java中的ForkJoin是什么及怎么调用

    下载Word文档到电脑,方便收藏和打印~

    下载Word文档

    猜你喜欢

    Java中的ForkJoin是什么及怎么调用

    本篇内容主要讲解“Java中的ForkJoin是什么及怎么调用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java中的ForkJoin是什么及怎么调用”吧!什么是ForkJoin?ForkJo
    2023-06-30

    java中@SuppressWarnings的概念是什么及怎么用

    本文小编为大家详细介绍“java中@SuppressWarnings的概念是什么及怎么用”,内容详细,步骤清晰,细节处理妥当,希望这篇“java中@SuppressWarnings的概念是什么及怎么用”文章能帮助大家解决疑惑,下面跟着小编的
    2023-06-30

    java中groovy调用的方法是什么

    在Java中调用Groovy方法,可以使用GroovyShell类来执行Groovy代码。具体步骤如下:创建GroovyShell对象:GroovyShell shell = new GroovyShell();使用GroovyShell对
    java中groovy调用的方法是什么
    2024-03-11

    Java中ThreadLocal的原理是什么及怎么使用

    这篇文章主要介绍“Java中ThreadLocal的原理是什么及怎么使用”,在日常操作中,相信很多人在Java中ThreadLocal的原理是什么及怎么使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Jav
    2023-07-06

    ChatGPT介绍及Java API调用的方法是什么

    本篇内容介绍了“ChatGPT介绍及Java API调用的方法是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!ChatGPT的基本介绍C
    2023-07-05

    go语言中函数的定义是什么及怎么调用

    本文小编为大家详细介绍“go语言中函数的定义是什么及怎么调用”,内容详细,步骤清晰,细节处理妥当,希望这篇“go语言中函数的定义是什么及怎么调用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。在go语言中,函数是组
    2023-07-04

    Java NIO是什么及怎么使用

    Java NIO(New IO)是Java 1.4版本中引入的一种用于替代传统Java IO的新的输入输出API。NIO提供了更高效、更灵活的IO操作方式,并且支持非阻塞IO模型。Java NIO的核心概念是通道(Channel)和缓冲区(
    2023-09-27

    Java中回调函数的作用是什么

    这期内容当中小编将会给大家带来有关Java中回调函数的作用是什么,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。1、先定义一个接口,规定回答问题的条件是打我手机public interface CallBa
    2023-06-17

    java ffmpeg调用的方法是什么

    Java中调用FFmpeg的方法是使用Java的外部命令执行功能来执行FFmpeg命令。可以使用Java的ProcessBuilder类来创建一个外部进程来执行FFmpeg命令。以下是一个简单的示例代码:import java.io.I
    2023-10-23

    golang调用java的方法是什么

    在Go语言中调用Java方法可以通过使用Go的Java调用库,例如gojni或gojava。这些库允许Go代码与Java代码进行交互。以下是使用gojni库调用Java方法的简单示例:首先,确保已经安装了gojni库。可以使用以下命令进行
    2023-10-27

    java调用golang的方法是什么

    Java调用Golang的方法通常使用CGO(C语言调用Go函数)技术实现。CGO是Go语言提供的一种机制,允许Go代码与C/C++代码进行互操作。要在Java中调用Golang的方法,可以按照以下步骤进行操作:1.首先,将Golang代码
    2023-10-20

    java调用kotlin的方法是什么

    在Java中调用Kotlin方法与调用Java方法类似,需要使用Kotlin类的实例来调用方法。但是在Java中调用Kotlin方法时,需要注意以下几点:1. 导入Kotlin的类:在Java中调用Kotlin方法之前,需要先导入Kotli
    2023-08-16

    Java继承的概念是什么及怎么用

    这篇文章主要讲解了“Java继承的概念是什么及怎么用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java继承的概念是什么及怎么用”吧!继承的概念继承就是子类继承父类的特征和行为,使得子类对
    2023-06-30

    JavaScript中的回调函数是什么及如何用

    今天小编给大家分享一下JavaScript中的回调函数是什么及如何用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。1.什么是
    2023-07-04

    Java中AQS的原理及作用是什么

    这篇文章主要介绍“Java中AQS的原理及作用是什么”,在日常操作中,相信很多人在Java中AQS的原理及作用是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java中AQS的原理及作用是什么”的疑惑有所
    2023-06-15

    Netty的Handler链调用机制是什么及怎么组织

    这篇“Netty的Handler链调用机制是什么及怎么组织”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Netty的Hand
    2023-07-05

    Java异步调用的方法是什么

    这篇文章主要讲解了“Java异步调用的方法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java异步调用的方法是什么”吧!一、创建线程@Testpublic void test0()
    2023-06-27

    java调用接口的原理是什么

    Java调用接口的原理是通过接口的实现类来实现接口的方法。当一个类实现了一个接口,它必须实现接口中声明的所有方法。然后可以通过创建实现类的对象来调用接口中的方法。在Java中,接口是一种约定,它定义了一组方法的签名,但没有提供方法的实现。一
    2023-10-09

    java跨类调用的方法是什么

    在Java中,跨类调用方法有两种常用的方式:1. 实例化对象调用:通过创建对象的方式实例化一个类,然后通过该对象调用该类的方法。例如:```java// 定义一个类public class MyClass {public void myMe
    2023-08-15

    Vue中的插槽是什么及怎么用

    这篇文章主要介绍了Vue中的插槽是什么及怎么用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Vue中的插槽是什么及怎么用文章都会有所收获,下面我们一起来看看吧。在 Vue 中,插槽是个很强大的功能,它可以使封装
    2023-06-30

    编程热搜

    • Python 学习之路 - Python
      一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
      Python 学习之路 - Python
    • chatgpt的中文全称是什么
      chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
      chatgpt的中文全称是什么
    • C/C++中extern函数使用详解
    • C/C++可变参数的使用
      可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
      C/C++可变参数的使用
    • css样式文件该放在哪里
    • php中数组下标必须是连续的吗
    • Python 3 教程
      Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
      Python 3 教程
    • Python pip包管理
      一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
      Python pip包管理
    • ubuntu如何重新编译内核
    • 改善Java代码之慎用java动态编译

    目录