欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

ThreadPoolExecutor參數(shù)含義及源碼執(zhí)行流程詳解

 更新時(shí)間:2022年11月08日 10:47:42   作者:Crave  
這篇文章主要為大家介紹了ThreadPoolExecutor參數(shù)含義及源碼執(zhí)行流程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

背景

線程池是為了避免線程頻繁的創(chuàng)建和銷(xiāo)毀帶來(lái)的性能消耗,而建立的一種池化技術(shù),它是把已創(chuàng)建的線程放入“池”中,當(dāng)有任務(wù)來(lái)臨時(shí)就可以重用已有的線程,無(wú)需等待創(chuàng)建的過(guò)程,這樣就可以有效提高程序的響應(yīng)速度。但如果要說(shuō)線程池的話一定離不開(kāi) ThreadPoolExecutor ,在阿里巴巴的《Java 開(kāi)發(fā)手冊(cè)》中是這樣規(guī)定線程池的:

線程池不允許使用 Executors 去創(chuàng)建,而是通過(guò) ThreadPoolExecutor 的方式,這樣的處理方式讓寫(xiě)的讀者更加明確線程池的運(yùn)行規(guī)則,規(guī)避資源耗盡的風(fēng)險(xiǎn)。

說(shuō)明:Executors 返回的線程池對(duì)象的弊端如下:

FixedThreadPool 和 SingleThreadPool:允許的請(qǐng)求隊(duì)列長(zhǎng)度為 Integer.MAX_VALUE,可能會(huì)堆積大量的請(qǐng)求,從而導(dǎo)致 OOM。CachedThreadPool 和 ScheduledThreadPool:允許的創(chuàng)建線程數(shù)量為 Integer.MAX_VALUE,可能會(huì)創(chuàng)建大量的線程,從而導(dǎo)致 OOM。

其實(shí)當(dāng)我們?nèi)タ?Executors 的源碼會(huì)發(fā)現(xiàn),Executors.newFixedThreadPool()、Executors.newSingleThreadExecutor() 和 Executors.newCachedThreadPool() 等方法的底層都是通過(guò) ThreadPoolExecutor 實(shí)現(xiàn)的,所以本課時(shí)我們就重點(diǎn)來(lái)了解一下 ThreadPoolExecutor 的相關(guān)知識(shí),比如它有哪些核心的參數(shù)?它是如何工作的?

典型回答

ThreadPoolExecutor 的核心參數(shù)指的是它在構(gòu)建時(shí)需要傳遞的參數(shù),其構(gòu)造方法如下所示:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
        // maximumPoolSize 必須大于 0,且必須大于 corePoolSize
        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;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

第 1 個(gè)參數(shù):corePoolSize 表示線程池的常駐核心線程數(shù)。如果設(shè)置為 0,則表示在沒(méi)有任何任務(wù)時(shí),銷(xiāo)毀線程池;如果大于 0,即使沒(méi)有任務(wù)時(shí)也會(huì)保證線程池的線程數(shù)量等于此值。但需要注意,此值如果設(shè)置的比較小,則會(huì)頻繁的創(chuàng)建和銷(xiāo)毀線程(創(chuàng)建和銷(xiāo)毀的原因會(huì)在本課時(shí)的下半部分講到);如果設(shè)置的比較大,則會(huì)浪費(fèi)系統(tǒng)資源,所以開(kāi)發(fā)者需要根據(jù)自己的實(shí)際業(yè)務(wù)來(lái)調(diào)整此值。

第 2 個(gè)參數(shù):maximumPoolSize 表示線程池在任務(wù)最多時(shí),最大可以創(chuàng)建的線程數(shù)。官方規(guī)定此值必須大于 0,也必須大于等于 corePoolSize,此值只有在任務(wù)比較多,且不能存放在任務(wù)隊(duì)列時(shí),才會(huì)用到。

第 3 個(gè)參數(shù):keepAliveTime 表示線程的存活時(shí)間,當(dāng)線程池空閑時(shí)并且超過(guò)了此時(shí)間,多余的線程就會(huì)銷(xiāo)毀,直到線程池中的線程數(shù)量銷(xiāo)毀的等于 corePoolSize 為止,如果 maximumPoolSize 等于 corePoolSize,那么線程池在空閑的時(shí)候也不會(huì)銷(xiāo)毀任何線程。

第 4 個(gè)參數(shù):unit 表示存活時(shí)間的單位,它是配合 keepAliveTime 參數(shù)共同使用的。

第 5 個(gè)參數(shù):workQueue 表示線程池執(zhí)行的任務(wù)隊(duì)列,當(dāng)線程池的所有線程都在處理任務(wù)時(shí),如果來(lái)了新任務(wù)就會(huì)緩存到此任務(wù)隊(duì)列中排隊(duì)等待執(zhí)行。

第 6 個(gè)參數(shù):threadFactory 表示線程的創(chuàng)建工廠,此參數(shù)一般用的比較少,我們通常在創(chuàng)建線程池時(shí)不指定此參數(shù),它會(huì)使用默認(rèn)的線程創(chuàng)建工廠的方法來(lái)創(chuàng)建線程,源代碼如下:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
    // Executors.defaultThreadFactory() 為默認(rèn)的線程創(chuàng)建工廠
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}
public static ThreadFactory defaultThreadFactory() {
    return new DefaultThreadFactory();
}
// 默認(rèn)的線程創(chuàng)建工廠,需要實(shí)現(xiàn) ThreadFactory 接口
static class DefaultThreadFactory implements ThreadFactory {
    private static final AtomicInteger poolNumber = new AtomicInteger(1);
    private final ThreadGroup group;
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;
    DefaultThreadFactory() {
        SecurityManager s = System.getSecurityManager();
        group = (s != null) ? s.getThreadGroup() :
                              Thread.currentThread().getThreadGroup();
        namePrefix = "pool-" +
                      poolNumber.getAndIncrement() +
                     "-thread-";
    }
    // 創(chuàng)建線程
    public Thread newThread(Runnable r) {
        Thread t = new Thread(group, r,
                              namePrefix + threadNumber.getAndIncrement(),
                              0);
        if (t.isDaemon()) 
            t.setDaemon(false); // 創(chuàng)建一個(gè)非守護(hù)線程
        if (t.getPriority() != Thread.NORM_PRIORITY)
            t.setPriority(Thread.NORM_PRIORITY); // 線程優(yōu)先級(jí)設(shè)置為默認(rèn)值
        return t;
    }
}

我們也可以自定義一個(gè)線程工廠,通過(guò)實(shí)現(xiàn) ThreadFactory 接口來(lái)完成,這樣就可以自定義線程的名稱或線程執(zhí)行的優(yōu)先級(jí)了。

第 7 個(gè)參數(shù):RejectedExecutionHandler 表示指定線程池的拒絕策略,當(dāng)線程池的任務(wù)已經(jīng)在緩存隊(duì)列 workQueue 中存儲(chǔ)滿了之后,并且不能創(chuàng)建新的線程來(lái)執(zhí)行此任務(wù)時(shí),就會(huì)用到此拒絕策略,它屬于一種限流保護(hù)的機(jī)制。

線程池的工作流程要從它的執(zhí)行方法 execute() 說(shuō)起,源碼如下:

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    int c = ctl.get();
    // 當(dāng)前工作的線程數(shù)小于核心線程數(shù)
    if (workerCountOf(c) < corePoolSize) {
        // 創(chuàng)建新的線程執(zhí)行此任務(wù)
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    // 檢查線程池是否處于運(yùn)行狀態(tài),如果是則把任務(wù)添加到隊(duì)列
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        // 再出檢查線程池是否處于運(yùn)行狀態(tài),防止在第一次校驗(yàn)通過(guò)后線程池關(guān)閉
        // 如果是非運(yùn)行狀態(tài),則將剛加入隊(duì)列的任務(wù)移除
        if (! isRunning(recheck) && remove(command))
            reject(command);
        // 如果線程池的線程數(shù)為 0 時(shí)(當(dāng) corePoolSize 設(shè)置為 0 時(shí)會(huì)發(fā)生)
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false); // 新建線程執(zhí)行任務(wù)
    }
    // 核心線程都在忙且隊(duì)列都已爆滿,嘗試新啟動(dòng)一個(gè)線程執(zhí)行失敗
    else if (!addWorker(command, false)) 
        // 執(zhí)行拒絕策略
        reject(command);
}

其中 addWorker(Runnable firstTask, boolean core) 方法的參數(shù)說(shuō)明如下:

  • firstTask,線程應(yīng)首先運(yùn)行的任務(wù),如果沒(méi)有則可以設(shè)置為 null;
  • core,判斷是否可以創(chuàng)建線程的閥值(最大值),如果等于 true 則表示使用 corePoolSize 作為閥值,false 則表示使用maximumPoolSize 作為閥值。

考點(diǎn)分析

線程池任務(wù)執(zhí)行的主要流程,可以參考以下流程圖:

與 ThreadPoolExecutor 相關(guān)的面試題還有以下幾個(gè):

  • ThreadPoolExecutor 的執(zhí)行方法有幾種?它們有什么區(qū)別?
  • 什么是線程的拒絕策略?
  • 拒絕策略的分類(lèi)有哪些?
  • 如何自定義拒絕策略?
  • ThreadPoolExecutor 能不能實(shí)現(xiàn)擴(kuò)展?如何實(shí)現(xiàn)擴(kuò)展?

知識(shí)拓展

execute() VS submit()

execute() 和 submit() 都是用來(lái)執(zhí)行線程池任務(wù)的,它們最主要的區(qū)別是,submit() 方法可以接收線程池執(zhí)行的返回值,而 execute() 不能接收返回值。

來(lái)看兩個(gè)方法的具體使用:

ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 10, 10L,
        TimeUnit.SECONDS, new LinkedBlockingQueue(20));
// execute 使用
executor.execute(new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello, execute.");
    }
});
// submit 使用
Future<String> future = executor.submit(new Callable<String>() {
    @Override
    public String call() throws Exception {
        System.out.println("Hello, submit.");
        return "Success";
    }
});
System.out.println(future.get());

以上程序執(zhí)行結(jié)果如下:

Hello, submit.
Hello, execute.
Success

從以上結(jié)果可以看出 submit() 方法可以配合 Futrue 來(lái)接收線程執(zhí)行的返回值。它們的另一個(gè)區(qū)別是 execute() 方法屬于 Executor 接口的方法,而 submit() 方法則是屬于 ExecutorService 接口的方法,它們的繼承關(guān)系如下圖所示:

線程池的拒絕策略

當(dāng)線程池中的任務(wù)隊(duì)列已經(jīng)被存滿,再有任務(wù)添加時(shí)會(huì)先判斷當(dāng)前線程池中的線程數(shù)是否大于等于線程池的最大值,如果是,則會(huì)觸發(fā)線程池的拒絕策略。

Java 自帶的拒絕策略有 4 種:

  • AbortPolicy,終止策略,線程池會(huì)拋出異常并終止執(zhí)行,它是默認(rèn)的拒絕策略;
  • CallerRunsPolicy,把任務(wù)交給當(dāng)前線程來(lái)執(zhí)行;
  • DiscardPolicy,忽略此任務(wù)(最新的任務(wù));
  • DiscardOldestPolicy,忽略最早的任務(wù)(最先加入隊(duì)列的任務(wù))。

例如,我們來(lái)演示一個(gè) AbortPolicy 的拒絕策略,代碼如下:

ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 3, 10,
        TimeUnit.SECONDS, new LinkedBlockingQueue<>(2),
        new ThreadPoolExecutor.AbortPolicy()); // 添加 AbortPolicy 拒絕策略
for (int i = 0; i < 6; i++) {
    executor.execute(() -> {
        System.out.println(Thread.currentThread().getName());
    });
}

以上程序的執(zhí)行結(jié)果:

pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-3
pool-1-thread-2
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.lagou.interview.ThreadPoolExample$$Lambda$1/1096979270@448139f0 rejected from java.util.concurrent.ThreadPoolExecutor@7cca494b[Running, pool size = 3, active threads = 3, queued tasks = 2, completed tasks = 0]
 at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
 at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
 at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
 at com.lagou.interview.ThreadPoolExample.rejected(ThreadPoolExample.java:35)
 at com.lagou.interview.ThreadPoolExample.main(ThreadPoolExample.java:26)

可以看出當(dāng)?shù)?6 個(gè)任務(wù)來(lái)的時(shí)候,線程池則執(zhí)行了AbortPolicy 拒絕策略,拋出了異常。因?yàn)殛?duì)列最多存儲(chǔ) 2 個(gè)任務(wù),最大可以創(chuàng)建 3 個(gè)線程來(lái)執(zhí)行任務(wù)(2+3=5),所以當(dāng)?shù)?6 個(gè)任務(wù)來(lái)的時(shí)候,此線程池就“忙”不過(guò)來(lái)了。

自定義拒絕策略

自定義拒絕策略只需要新建一個(gè) RejectedExecutionHandler 對(duì)象,然后重寫(xiě)它的 rejectedExecution() 方法即可,如下代碼所示:

ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 3, 10,
        TimeUnit.SECONDS, new LinkedBlockingQueue<>(2),
        new RejectedExecutionHandler() {  // 添加自定義拒絕策略
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                // 業(yè)務(wù)處理方法
                System.out.println("執(zhí)行自定義拒絕策略");
            }
        });
for (int i = 0; i < 6; i++) {
    executor.execute(() -> {
        System.out.println(Thread.currentThread().getName());
    });
}

以上代碼執(zhí)行的結(jié)果如下:

執(zhí)行自定義拒絕策略
pool-1-thread-2
pool-1-thread-3
pool-1-thread-1
pool-1-thread-1
pool-1-thread-2

可以看出線程池執(zhí)行了自定義的拒絕策略,我們可以在 rejectedExecution 中添加自己業(yè)務(wù)處理的代碼。

ThreadPoolExecutor 擴(kuò)展

ThreadPoolExecutor 的擴(kuò)展主要是通過(guò)重寫(xiě)它的 beforeExecute() 和 afterExecute() 方法實(shí)現(xiàn)的,我們可以在擴(kuò)展方法中添加日志或者實(shí)現(xiàn)數(shù)據(jù)統(tǒng)計(jì),比如統(tǒng)計(jì)線程的執(zhí)行時(shí)間,如下代碼所示:

public class ThreadPoolExtend {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 線程池?cái)U(kuò)展調(diào)用
        MyThreadPoolExecutor executor = new MyThreadPoolExecutor(2, 4, 10,
                TimeUnit.SECONDS, new LinkedBlockingQueue());
        for (int i = 0; i < 3; i++) {
            executor.execute(() -> {
                Thread.currentThread().getName();
            });
        }
    }
   /**
     * 線程池?cái)U(kuò)展
     */
    static class MyThreadPoolExecutor extends ThreadPoolExecutor {
        // 保存線程執(zhí)行開(kāi)始時(shí)間
        private final ThreadLocal<Long> localTime = new ThreadLocal<>();
        public MyThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
                            TimeUnit unit, BlockingQueue<Runnable> workQueue) {
    super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
        /**
         * 開(kāi)始執(zhí)行之前
         * @param t 線程
         * @param r 任務(wù)
         */
        @Override
        protected void beforeExecute(Thread t, Runnable r) {
            Long sTime = System.nanoTime(); // 開(kāi)始時(shí)間 (單位:納秒)
            localTime.set(sTime);
            System.out.println(String.format("%s | before | time=%s",
                    t.getName(), sTime));
            super.beforeExecute(t, r);
        }
        /**
         * 執(zhí)行完成之后
         * @param r 任務(wù)
         * @param t 拋出的異常
         */
        @Override
        protected void afterExecute(Runnable r, Throwable t) {
            Long eTime = System.nanoTime(); // 結(jié)束時(shí)間 (單位:納秒)
            Long totalTime = eTime - localTime.get(); // 執(zhí)行總時(shí)間
            System.out.println(String.format("%s | after | time=%s | 耗時(shí):%s 毫秒",
                    Thread.currentThread().getName(), eTime, (totalTime / 1000000.0)));
            super.afterExecute(r, t);
        }
    }
}

以上程序的執(zhí)行結(jié)果如下所示:

pool-1-thread-1 | before | time=4570298843700
pool-1-thread-2 | before | time=4570298840000
pool-1-thread-1 | after | time=4570327059500 | 耗時(shí):28.2158 毫秒
pool-1-thread-2 | after | time=4570327138100 | 耗時(shí):28.2981 毫秒
pool-1-thread-1 | before | time=4570328467800
pool-1-thread-1 | after | time=4570328636800 | 耗時(shí):0.169 毫秒

小結(jié)

最后我們總結(jié)一下:線程池的使用必須要通過(guò) ThreadPoolExecutor 的方式來(lái)創(chuàng)建,這樣才可以更加明確線程池的運(yùn)行規(guī)則,規(guī)避資源耗盡的風(fēng)險(xiǎn)。同時(shí),也介紹了 ThreadPoolExecutor 的七大核心參數(shù),包括核心線程數(shù)和最大線程數(shù)之間的區(qū)別,當(dāng)線程池的任務(wù)隊(duì)列沒(méi)有可用空間且線程池的線程數(shù)量已經(jīng)達(dá)到了最大線程數(shù)時(shí),則會(huì)執(zhí)行拒絕策略,Java 自動(dòng)的拒絕策略有 4 種,用戶也可以通過(guò)重寫(xiě) rejectedExecution() 來(lái)自定義拒絕策略,我們還可以通過(guò)重寫(xiě) beforeExecute() 和 afterExecute() 來(lái)實(shí)現(xiàn) ThreadPoolExecutor 的擴(kuò)展功能。

以上就是ThreadPoolExecutor參數(shù)含義及源碼執(zhí)行流程詳解的詳細(xì)內(nèi)容,更多關(guān)于ThreadPoolExecutor執(zhí)行流程的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解Spring中接口的bean是如何注入的

    詳解Spring中接口的bean是如何注入的

    這篇文章主要介紹了詳解Spring中接口的bean是如何注入的的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Spring具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • Java實(shí)現(xiàn)猜字小游戲

    Java實(shí)現(xiàn)猜字小游戲

    這篇文章給大家分享小編隨手寫(xiě)的猜字小游戲,基于java代碼寫(xiě)的,感興趣的朋友跟隨小編一起看看吧
    2019-11-11
  • 誤將.idea文件提交至git后刪除的操作方法

    誤將.idea文件提交至git后刪除的操作方法

    這篇文章主要介紹了誤將.idea文件提交至git后刪除的操作方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-08-08
  • 淺談Hibernate n+1問(wèn)題

    淺談Hibernate n+1問(wèn)題

    這篇文章主要介紹了淺談Hibernate n+1問(wèn)題,怎么解決n+1問(wèn)題,文中也作了簡(jiǎn)要分析,小編覺(jué)得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2018-02-02
  • 指定springboot的jar運(yùn)行內(nèi)存方式

    指定springboot的jar運(yùn)行內(nèi)存方式

    這篇文章主要介紹了指定springboot的jar運(yùn)行內(nèi)存方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • Springboot和Jpa實(shí)現(xiàn)學(xué)生CRUD操作代碼實(shí)例

    Springboot和Jpa實(shí)現(xiàn)學(xué)生CRUD操作代碼實(shí)例

    這篇文章主要介紹了Springboot和Jpa實(shí)現(xiàn)學(xué)生CRUD操作代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-03-03
  • Java?3年面試經(jīng)驗(yàn)告訴你Mybatis是如何進(jìn)行分頁(yè)的

    Java?3年面試經(jīng)驗(yàn)告訴你Mybatis是如何進(jìn)行分頁(yè)的

    這篇文章主要介紹了Java?3年面試經(jīng)驗(yàn)告訴你Mybatis是如何進(jìn)行分頁(yè)的,對(duì)于任何ORM框架,分頁(yè)的實(shí)現(xiàn)邏輯無(wú)外乎兩種,不管怎么包裝,最終給到開(kāi)發(fā)者的,只是使用上的差異而已,本文給大家講解的很明白,感興趣的朋友一起看看吧
    2022-09-09
  • SpringBoot中讀取jar包中的resources目錄下的文件的三種方式

    SpringBoot中讀取jar包中的resources目錄下的文件的三種方式

    這篇文章給大家總結(jié)了SpringBoot讀取 jar 包中的 resources 目錄下的文件的三種方式,文中有詳細(xì)的代碼示例供大家參考,,需要的朋友可以參考下
    2023-06-06
  • Java實(shí)現(xiàn)二叉搜索樹(shù)的插入、刪除功能

    Java實(shí)現(xiàn)二叉搜索樹(shù)的插入、刪除功能

    這篇文章主要介紹了Java實(shí)現(xiàn)二叉搜索樹(shù)的插入、刪除,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-01-01
  • Java中的日期時(shí)間處理及格式化處理

    Java中的日期時(shí)間處理及格式化處理

    這篇文章主要介紹了Java中的日期時(shí)間處理及格式化處理方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08

最新評(píng)論