Java Review - 并发编程_ScheduledThreadPoolExecutor原理&源码剖析

简介: Java Review - 并发编程_ScheduledThreadPoolExecutor原理&源码剖析

195d03d17afc4a928bc581f313b01dfe.png

概述


Java Review - 并发编程_ThreadPoolExecutor原理&源码剖析 我们复习了Java中线程池ThreadPoolExecutor的原理,ThreadPoolExecutor只是Executors工具类的一部分功能。


下面来介绍另外一部分功能,也就是ScheduledThreadPoolExecutor的实现,这是一个可以在指定一定延迟时间后或者定时进行任务调度执行的线程池。


类结构


da914a9e1ed347dfbea59ca31e32d555.jpg


Executors其实是个工具类,它提供了好多静态方法,可根据用户的选择返回不同的线程池实例。


ScheduledThreadPoolExecutor继承了ThreadPoolExecutor并实现了ScheduledExecutorService接口。


线程池队列是DelayedWorkQueue,其和DelayedQueue类似,是一个延迟队列


ScheduledFutureTask是具有返回值的任务,继承自FutureTask。FutureTask的内部有一个变量state用来表示任务的状态,一开始状态为NEW,所有状态为

    private static final int NEW          = 0; // 初始状态
    private static final int COMPLETING   = 1; // 执行中
    private static final int NORMAL       = 2; // 正常运行结束状态
    private static final int EXCEPTIONAL  = 3; // 运行中异常
    private static final int CANCELLED    = 4; // 任务被取消
    private static final int INTERRUPTING = 5; // 任务正在被中断
    private static final int INTERRUPTED  = 6; // 任务已经被中断

可能的任务状态转换路径为

NEN-> COMPLETING-> NORMAL//初始状态->执行中ー>正常结東
NEN-> COMPILETING-> EXCEPTIONAL//初始状态->执行中ー>执行异常
NEN-> CANCELLED//初始状态一>任务取消
NEN-> INTERRUPTING-> INTERRUPTED//初始状态->被中断中->被中断


ScheduledFutureTask内部还有一个变量period用来表示任务的类型,任务类型如下


period=0,说明当前任务是一次性的,执行完毕后就退出了。


period为负数,说明当前任务为fixed-delay任务,是固定延迟的定时可重复执行任务。


period为正数,说明当前任务为fixed-rate任务,是固定频率的定时可重复执行任务

30ad52c9656f4af282bad85cec2ef73f.png

  • ScheduledThreadPoolExecutor的一个构造函数如下,由该构造函数可知线程池队列是DelayedWorkQueue。
    /**
     * Creates a new {@code ScheduledThreadPoolExecutor} with the
     * given core pool size.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @throws IllegalArgumentException if {@code corePoolSize < 0}
     */
  // 使用改造后的DelayQueue
    public ScheduledThreadPoolExecutor(int corePoolSize) {
      // 调用父类ThreadPoolExecutor的构造函数
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }
   public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }


核心方法&源码解析


schedule(Runnable command, long delay,TimeUnit unit)


该方法的作用是提交一个延迟执行的任务,任务从提交时间算起延迟单位为unit的delay时间后开始执行。提交的任务不是周期性任务,任务只会执行一次.

    /**
     * @throws RejectedExecutionException {@inheritDoc}
     * @throws NullPointerException       {@inheritDoc}
     */
    public ScheduledFuture<?> schedule(Runnable command,
                                       long delay,
                                       TimeUnit unit) {
        //  1 参数校验                               
        if (command == null || unit == null)
            throw new NullPointerException();
        //  2 任务转换 
        RunnableScheduledFuture<?> t = decorateTask(command,
            new ScheduledFutureTask<Void>(command, null,
                                          triggerTime(delay, unit)));
        // 3 添加任务到延迟队列                                  
        delayedExecute(t);
        return t;
    }



代码(1)进行参数校验,如果command或者unit为null,则抛出NPE异常。


代码(2)装饰任务,把提交的command任务转换为ScheduledFutureTask。


ScheduledFutureTask是具体放入延迟队列里面的东西。由于是延迟任务,所以ScheduledFutureTask实现了long getDelay(TimeUnit unit)和int compareTo(Delayed other)方法。triggerTime方法将延迟时间转换为绝对时间,也就是把当前时间的纳秒数加上延迟的纳秒数后的long型值。


ScheduledFutureTask的构造函数如下。

  /**
         * Creates a one-shot action with given nanoTime-based trigger time.
         */
        ScheduledFutureTask(Runnable r, V result, long ns) {
          // 调用父类FutureTask的构造函数
            super(r, result);
            this.time = ns;
            this.period = 0; // 0 说明是一次性任务
            this.sequenceNumber = sequencer.getAndIncrement();
        }

在构造函数内部首先调用了父类FutureTask的构造函数,父类FutureTask的构造函数代码如下。

    public FutureTask(Runnable runnable, V result) {
      // 通过适配器把runnable转换为callable
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable 设置状态为NEW
    }

FutureTask中的任务被转换为Callable类型后,被保存到了变量this.callable里面,并设置FutureTask的任务状态为NEW。


然后在ScheduledFutureTask构造函数内部设置time为上面说的绝对时间。需要注意,这里period的值为0,这说明当前任务为一次性的任务,不是定时反复执行任务。其中long getDelay(TimeUnit unit)方法的代码如下(该方法用来计算当前任务还有多少时间就过期了)。

 public long getDelay(TimeUnit unit) {
      // 装饰后的时间 - 当前时间 = 即将过期剩余时间
            return unit.convert(time - now(), NANOSECONDS);
        }
        public int compareTo(Delayed other) {
            if (other == this) // compare zero if same object
                return 0;
            if (other instanceof ScheduledFutureTask) {
                ScheduledFutureTask<?> x = (ScheduledFutureTask<?>)other;
                long diff = time - x.time;
                if (diff < 0)
                    return -1;
                else if (diff > 0)
                    return 1;
                else if (sequenceNumber < x.sequenceNumber)
                    return -1;
                else
                    return 1;
            }
            long diff = getDelay(NANOSECONDS) - other.getDelay(NANOSECONDS);
            return (diff < 0) ? -1 : (diff > 0) ? 1 : 0;
        }

compareTo的作用是加入元素到延迟队列后,在内部建立或者调整堆时会使用该元素的compareTo方法与队列里面其他元素进行比较,让最快要过期的元素放到队首。所以无论什么时候向队列里面添加元素,队首的元素都是最快要过期的元素。


代码(3)将任务添加到延迟队列,delayedExecute的代码如下。

 /**
     * Main execution method for delayed or periodic tasks.  If pool
     * is shut down, rejects the task. Otherwise adds task to queue
     * and starts a thread, if necessary, to run it.  (We cannot
     * prestart the thread to run the task because the task (probably)
     * shouldn't be run yet.)  If the pool is shut down while the task
     * is being added, cancel and remove it if required by state and
     * run-after-shutdown parameters.
     *
     * @param task the task
     */
    private void delayedExecute(RunnableScheduledFuture<?> task) {
      // 4 如果线程池拐臂了,则执行线程执行拒绝策略
        if (isShutdown())
            reject(task);
        else {
          // 5 添加任务到延迟队列
            super.getQueue().add(task);
            // 6 再次校验
            if (isShutdown() &&
                !canRunInCurrentRunState(task.isPeriodic()) &&
                remove(task))
                task.cancel(false);
            else
              // 7 确保至少一个线程在处理任务
                ensurePrestart();
        }
    }


代码(4)首先判断当前线程池是否已经关闭了,如果已经关闭则执行线程池的拒绝策略 ,否则执行代码(5)将任务添加到延迟队列

添加完毕后还要重新检查线程池是否被关闭了,如果已经关闭则从延迟队列里面删除刚才添加的任务,但是此时有可能线程池中的线程已经从任务队列里面移除了该任务,也就是该任务已经在执行了,所以还需要调用任务的cancle方法取消任务。

如果代码(6)判断结果为false,则会执行代码(7)确保至少有一个线程在处理任务,即使核心线程数corePoolSize被设置为0

    /**
     * Same as prestartCoreThread except arranges that at least one
     * thread is started even if corePoolSize is 0.
     */
    void ensurePrestart() {
        int wc = workerCountOf(ctl.get());
        // 增加核心线程数
        if (wc < corePoolSize)
            addWorker(null, true);
        // 如果初始化corePoolSize=0,则也添加一个线程    
        else if (wc == 0)
            addWorker(null, false);
    }

如上代码首先获取线程池中的线程个数,如果线程个数小于核心线程数则新增一个线程,否则如果当前线程数为0则新增一个线程。


上面我们分析了如何向延迟队列添加任务,下面我们来看线程池里面的线程如何获取并执行任务。


前面说ThreadPoolExecutor时我们说过,具体执行任务的线程是Worker线程,Worker线程调用具体任务的run方法来执行。由于这里的任务是ScheduledFutureTask,所以我们下面看看ScheduledFutureTask的run方法

    /**
         * Overrides FutureTask version so as to reset/requeue if periodic.
         */
        public void run() {
          // 8 是否只执行一次 
            boolean periodic = isPeriodic();
            // 9 取消任务
            if (!canRunInCurrentRunState(periodic))
                cancel(false);
            // 10 只执行一次,调用schedule方法
            else if (!periodic)
                ScheduledFutureTask.super.run();
            // 11 定时执行    
            else if (ScheduledFutureTask.super.runAndReset()) {
              // 11.1 设置time=time+period
                setNextRunTime();
                // 11.2 重新加入该任务到delay队列
                reExecutePeriodic(outerTask);
            }
        }

代码(8)中的isPeriodic的作用是判断当前任务是一次性任务还是可重复执行的任务

public boolean isPeriodic() {
            return period != 0;
        }


  • 可以看到,其内部是通过period的值来判断的,由于转换任务在创建ScheduledFutureTask时传递的period的值为0 ,所以这里isPeriodic返回false。
  • 代码(9)判断当前任务是否应该被取消,canRunInCurrentRunState的代码如下
boolean canRunInCurrentRunState(boolean periodic) {
        return isRunningOrShutdown(periodic ?
                                   continueExistingPeriodicTasksAfterShutdown :
                                   executeExistingDelayedTasksAfterShutdown);
    }

传递的periodic的值为false,所以isRunningOrShutdown的参数为executeExisti ngDelayedTasksAfterShutdown。executeExistingDelayedTasksAfterShutdown默认为true,表示当其他线程调用了shutdown命令关闭了线程池后,当前任务还是要执行,否则如果为false,则当前任务要被取消。


由于periodic的值为false,所以执行代码(10)调用父类FutureTask的run方法具体执行任务。FutureTask的run方法的代码如下


  public void run() {
      // 12 
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        // 13     
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    // 13.1
                    setException(ex);
                }
                // 13.2
                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);
        }
    }


代码(12)判断如果任务状态不是NEW则直接返回,或者如果当前任务状态为NEW但是使用CAS设置当前任务的持有者为当前线程失败则直接返回


代码(13)具体调用callable的call方法执行任务。这里在调用前又判断了任务的状态是否为NEW,是为了避免在执行代码(12)后其他线程修改了任务的状态(比如取消了该任务)。\


如果任务执行成功则执行代码(13.2)修改任务状态,set方法的代码如下。

    protected void set(V v) {
      // 如果为NEW,设置为COMPLETING
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = v;
            // 设置当前任务的状态为NORMAL,也就是任务正常结束
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            finishCompletion();
        }
    }

如上代码首先使用CAS将当前任务的状态从NEW转换到COMPLETING。这里当有多个线程调用时只有一个线程会成功。成功的线程再通过UNSAFE.putOrderedInt设置任务的状态为正常结束状态,这里没有使用CAS是因为对于同一个任务只可能有一个线程运行到这里。

在这里使用putOrderedInt比使用CAS或者putLongvolatile效率要高,并且这里的场景不要求其他线程马上对设置的状态值可见。

在什么时候多个线程会同时执行CAS将当前任务的状态从NEW转换到COMPLETING?其实当同一个command被多次提交到线程池时就会存在这样的情况,因为同一个任务共享一个状态值state。


如果任务执行失败,则执行代码(13.1)。setException的代码如下,可见与set函数类似。

   protected void setException(Throwable t) {
      // 如果当前任务的状态为NEW,则设置为COMPLETING
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = t;
            // 设置当前任务的状态为EXCEPTIONAL,也就是任务非正常结束
            UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
            finishCompletion();
        }
    }

到这里代码(10)的逻辑执行完毕,一次性任务也就执行完毕了


scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit)


该方法的作用是,当任务执行完毕后,让其延迟固定时间后再次运行(fixed-delay任务)


  • initialDelay表示提交任务后延迟多少时间开始执行任务command
  • delay表示当任务执行完毕后延长多少时间后再次运行command任务
  • unit是initialDelay和delay的时间单位


任务会一直重复运行直到任务运行中抛出了异常,被取消了,或者关闭了线程池。

 /**
     * @throws RejectedExecutionException {@inheritDoc}
     * @throws NullPointerException       {@inheritDoc}
     * @throws IllegalArgumentException   {@inheritDoc}
     */
    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                     long initialDelay,
                                                     long delay,
                                                     TimeUnit unit) {
       // 14 参数校验                                              
        if (command == null || unit == null)
            throw new NullPointerException();
        if (delay <= 0)
            throw new IllegalArgumentException();
        // 15 任务转换   ,注意这里的 poeriod = -dealy < 0   【 unit.toNanos(-delay)】
        ScheduledFutureTask<Void> sft =
            new ScheduledFutureTask<Void>(command,
                                          null,
                                          triggerTime(initialDelay, unit),
                                          unit.toNanos(-delay));
        RunnableScheduledFuture<Void> t = decorateTask(command, sft);
        sft.outerTask = t;
        // 16 添加任务到队列
        delayedExecute(t);
        return t;
    }



代码(14)进行参数校验,校验失败则抛出异常

代码(15)将command任务转换为ScheduledFutureTask。这里需要注意的是,传递给ScheduledFutureTask的period变量的值为-delay,period<0说明该任务为可重复执行的任务。

然后代码(16)添加任务到延迟队列后返回。


将任务添加到延迟队列后线程池线程会从队列里面获取任务,然后调用ScheduledFutureTask的run方法执行。由于这里period<0,所以isPeriodic返回true,所以执行代码(11)。runAndReset的代码如下。

324b2b0e7909430c94580f223926a745.png


    /**
     * Executes the computation without setting its result, and then
     * resets this future to initial state, failing to do so if the
     * computation encounters an exception or is cancelled.  This is
     * designed for use with tasks that intrinsically execute more
     * than once.
     *
     * @return {@code true} if successfully run and reset
     */
    protected boolean runAndReset() {
      // 17 
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return false;
        // 18     
        boolean ran = false;
        int s = state;
        try {
            Callable<V> c = callable;
            if (c != null && s == NEW) {
                try {
                    c.call(); // don't set result
                    ran = true;
                } catch (Throwable ex) {
                    setException(ex);
                }
            }
        } 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
            s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
        return ran && s == NEW;
    }


该代码和FutureTask的run方法类似,只是任务正常执行完毕后不会设置任务的状态,这样做是为了让任务成为可重复执行的任务。


这里多了代码(19),这段代码判断如果当前任务正常执行完毕并且任务状态为NEW则返回true,否则返回false。 如果返回了true则执行代码(11.1)的setNextRunTime方法设置该任务下一次的执行时间。

  /**
         * Sets the next time to run for a periodic task.
         */
        private void setNextRunTime() {
            long p = period;
            if (p > 0) // ffixed-rate类型任务
                time += p;
            else // fixed-delay 类型任务 
                time = triggerTime(-p);
        }

这里p<0说明当前任务为fixed-delay类型任务。然后设置time为当前时间加上-p的时间,也就是延迟-p时间后再次执行。


fixed-delay类型的任务的执行原理为: 当添加一个任务到延迟队列后,等待initialDelay时间,任务就会过期,过期的任务就会被从队列移除,并执行。执行完毕后,会重新设置任务的延迟时间,然后再把任务放入延迟队列,循环往复。需要注意的是,如果一个任务在执行中抛出了异常,那么这个任务就结束了,但是不影响其他任务的执行。


scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit)


该方法相对起始时间点以固定频率调用指定的任务(fixed-rate任务)。当把任务提交到线程池并延迟initialDelay时间(时间单位为unit)后开始执行任务command 。然后从initialDelay+period时间点再次执行,而后在 initialDelay + 2 * period时间点再次执行,循环往复,直到抛出异常或者调用了任务的cancel方法取消了任务,或者关闭了线程池。


scheduleAtFixedRate的原理与scheduleWithFixedDelay类似,下面我们看下它们之间的不同点。


首先调用scheduleAtFixedRate的代码如下

    /**
     * @throws RejectedExecutionException {@inheritDoc}
     * @throws NullPointerException       {@inheritDoc}
     * @throws IllegalArgumentException   {@inheritDoc}
     */
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit) {
        if (command == null || unit == null)
            throw new NullPointerException();
        if (period <= 0)
            throw new IllegalArgumentException();
    // 装饰任务,注意这里的period=period>0 不是负的
        ScheduledFutureTask<Void> sft =
            new ScheduledFutureTask<Void>(command,
                                          null,
                                          triggerTime(initialDelay, unit),
                                          unit.toNanos(period));
        RunnableScheduledFuture<Void> t = decorateTask(command, sft);
        sft.outerTask = t;
        delayedExecute(t);
        return t;
    }


在如上代码中,在将fixed-rate类型的任务command转换为ScheduledFutureTask时设置period=period,不再是-period。


所以当前任务执行完毕后,调用setNextRunTime设置任务下次执行的时间时执行的是time += p而不再是time = triggerTime(-p)。


总结:相对于fixed-delay任务来说,fixed-rate方式执行规则为,时间为initdelday +n*period时启动任务,但是如果当前任务还没有执行完,下一次要执行任务的时间到了,则不会并发执行,下次要执行的任务会延迟执行,要等到当前任务执行完毕后再执行。


小结


ScheduledThreadPoolExecutor的实现原理,其内部使用DelayQueue来存放具体任务。任务分为三种,其中一次性执行任务执行完毕就结束了,fixed-delay任务保证同一个任务在多次执行之间间隔固定时间,fixed-rate任务保证按照固定的频率执行。任务类型使用period的值来区分。


b8d5a08f15b14eaab211257f75795ceb.png


相关文章
|
11天前
|
XML Java 编译器
Java注解的底层源码剖析与技术认识
Java注解(Annotation)是Java 5引入的一种新特性,它提供了一种在代码中添加元数据(Metadata)的方式。注解本身并不是代码的一部分,它们不会直接影响代码的执行,但可以在编译、类加载和运行时被读取和处理。注解为开发者提供了一种以非侵入性的方式为代码提供额外信息的手段,这些信息可以用于生成文档、编译时检查、运行时处理等。
45 7
|
23天前
|
数据采集 人工智能 Java
Java产科专科电子病历系统源码
产科专科电子病历系统,全结构化设计,实现产科专科电子病历与院内HIS、LIS、PACS信息系统、区域妇幼信息平台的三级互联互通,系统由门诊系统、住院系统、数据统计模块三部分组成,它管理了孕妇从怀孕开始到生产结束42天一系列医院保健服务信息。
28 4
|
13天前
|
安全 Java 程序员
深入理解Java内存模型与并发编程####
本文旨在探讨Java内存模型(JMM)的复杂性及其对并发编程的影响,不同于传统的摘要形式,本文将以一个实际案例为引子,逐步揭示JMM的核心概念,包括原子性、可见性、有序性,以及这些特性在多线程环境下的具体表现。通过对比分析不同并发工具类的应用,如synchronized、volatile关键字、Lock接口及其实现等,本文将展示如何在实践中有效利用JMM来设计高效且安全的并发程序。最后,还将简要介绍Java 8及更高版本中引入的新特性,如StampedLock,以及它们如何进一步优化多线程编程模型。 ####
20 0
|
4天前
|
存储 JavaScript 前端开发
基于 SpringBoot 和 Vue 开发校园点餐订餐外卖跑腿Java源码
一个非常实用的校园外卖系统,基于 SpringBoot 和 Vue 的开发。这一系统源于黑马的外卖案例项目 经过站长的进一步改进和优化,提供了更丰富的功能和更高的可用性。 这个项目的架构设计非常有趣。虽然它采用了SpringBoot和Vue的组合,但并不是一个完全分离的项目。 前端视图通过JS的方式引入了Vue和Element UI,既能利用Vue的快速开发优势,
39 13
|
17天前
|
缓存 监控 Java
Java线程池提交任务流程底层源码与源码解析
【11月更文挑战第30天】嘿,各位技术爱好者们,今天咱们来聊聊Java线程池提交任务的底层源码与源码解析。作为一个资深的Java开发者,我相信你一定对线程池并不陌生。线程池作为并发编程中的一大利器,其重要性不言而喻。今天,我将以对话的方式,带你一步步深入线程池的奥秘,从概述到功能点,再到背景和业务点,最后到底层原理和示例,让你对线程池有一个全新的认识。
47 12
|
12天前
|
JavaScript 安全 Java
java版药品不良反应智能监测系统源码,采用SpringBoot、Vue、MySQL技术开发
基于B/S架构,采用Java、SpringBoot、Vue、MySQL等技术自主研发的ADR智能监测系统,适用于三甲医院,支持二次开发。该系统能自动监测全院患者药物不良反应,通过移动端和PC端实时反馈,提升用药安全。系统涵盖规则管理、监测报告、系统管理三大模块,确保精准、高效地处理ADR事件。
|
18天前
|
缓存 Java 开发者
Java多线程并发编程:同步机制与实践应用
本文深入探讨Java多线程中的同步机制,分析了多线程并发带来的数据不一致等问题,详细介绍了`synchronized`关键字、`ReentrantLock`显式锁及`ReentrantReadWriteLock`读写锁的应用,结合代码示例展示了如何有效解决竞态条件,提升程序性能与稳定性。
59 6
|
14天前
|
人工智能 移动开发 安全
家政上门系统用户端、阿姨端源码,java家政管理平台源码
家政上门系统基于互联网技术,整合大数据分析、AI算法和现代通信技术,提供便捷高效的家政服务。涵盖保洁、月嫂、烹饪等多元化服务,支持多终端访问,具备智能匹配、在线支付、订单管理等功能,确保服务透明、安全,适用于家庭生活的各种需求场景,推动家政市场规范化发展。
|
19天前
|
设计模式 安全 Java
Java 多线程并发编程
Java多线程并发编程是指在Java程序中使用多个线程同时执行,以提高程序的运行效率和响应速度。通过合理管理和调度线程,可以充分利用多核处理器资源,实现高效的任务处理。本内容将介绍Java多线程的基础概念、实现方式及常见问题解决方法。
38 0
|
3天前
|
安全 Java API
java如何请求接口然后终止某个线程
通过本文的介绍,您应该能够理解如何在Java中请求接口并根据返回结果终止某个线程。合理使用标志位或 `interrupt`方法可以确保线程的安全终止,而处理好网络请求中的各种异常情况,可以提高程序的稳定性和可靠性。
27 6