Java線程池大小的設(shè)置方法實(shí)例
Java 中線程池創(chuàng)建的幾種方式
首先我們要先知道 Java 中創(chuàng)建線程池的方式,java中創(chuàng)建線程池的方式一般有兩種,如下所示:
- 通過(guò)Executors工廠方法創(chuàng)建
- 通過(guò)new ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)自定義創(chuàng)建
🐱🏍Executors 工廠方法創(chuàng)建
上代碼:
package com.base.demo.design.play;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* @Description: 線程池代碼
* @BelongsProject: base-demo-design
* @BelongsPackage: com.base.demo.design.play
* @Author: ChenYongJia
* @CreateTime: 2021-08-14 15:26
* @Email: chen87647213@163.com
* @Version: 1.0
*/
public class TestThreadPoolExecutor {
public static void main(String[] args) {
// 創(chuàng)建使用單個(gè)線程的線程池
ExecutorService es1 = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
es1.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "正在執(zhí)行任務(wù)");
}
});
}
// 創(chuàng)建使用固定線程數(shù)的線程池
ExecutorService es2 = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
es2.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "正在執(zhí)行任務(wù)");
}
});
}
// 創(chuàng)建一個(gè)會(huì)根據(jù)需要?jiǎng)?chuàng)建新線程的線程池
ExecutorService es3 = Executors.newCachedThreadPool();
for (int i = 0; i < 20; i++) {
es3.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "正在執(zhí)行任務(wù)");
}
});
}
// 創(chuàng)建擁有固定線程數(shù)量的定時(shí)線程任務(wù)的線程池
ScheduledExecutorService es4 = Executors.newScheduledThreadPool(2);
System.out.println("時(shí)間:" + System.currentTimeMillis());
for (int i = 0; i < 5; i++) {
es4.schedule(new Runnable() {
@Override
public void run() {
System.out.println("時(shí)間:"+System.currentTimeMillis()+"--"+Thread.currentThread().getName() + "正在執(zhí)行任務(wù)");
}
},3, TimeUnit.SECONDS);
}
// 創(chuàng)建只有一個(gè)線程的定時(shí)線程任務(wù)的線程池
ScheduledExecutorService es5 = Executors.newSingleThreadScheduledExecutor();
System.out.println("時(shí)間:" + System.currentTimeMillis());
for (int i = 0; i < 5; i++) {
es5.schedule(new Runnable() {
@Override
public void run() {
System.out.println("時(shí)間:"+System.currentTimeMillis()+"--"+Thread.currentThread().getName() + "正在執(zhí)行任務(wù)");
}
},3, TimeUnit.SECONDS);
}
}
}
運(yùn)行結(jié)果如下:
pool-1-thread-1正在執(zhí)行任務(wù)
pool-1-thread-1正在執(zhí)行任務(wù)
pool-1-thread-1正在執(zhí)行任務(wù)
pool-1-thread-1正在執(zhí)行任務(wù)
pool-1-thread-1正在執(zhí)行任務(wù)
pool-1-thread-1正在執(zhí)行任務(wù)
pool-1-thread-1正在執(zhí)行任務(wù)
pool-1-thread-1正在執(zhí)行任務(wù)
pool-1-thread-1正在執(zhí)行任務(wù)
pool-1-thread-1正在執(zhí)行任務(wù)
pool-2-thread-1正在執(zhí)行任務(wù)
pool-2-thread-2正在執(zhí)行任務(wù)
pool-2-thread-1正在執(zhí)行任務(wù)
pool-2-thread-3正在執(zhí)行任務(wù)
pool-2-thread-2正在執(zhí)行任務(wù)
pool-2-thread-3正在執(zhí)行任務(wù)
pool-2-thread-1正在執(zhí)行任務(wù)
pool-2-thread-3正在執(zhí)行任務(wù)
pool-2-thread-2正在執(zhí)行任務(wù)
pool-2-thread-1正在執(zhí)行任務(wù)
pool-3-thread-1正在執(zhí)行任務(wù)
pool-3-thread-2正在執(zhí)行任務(wù)
pool-3-thread-2正在執(zhí)行任務(wù)
pool-3-thread-3正在執(zhí)行任務(wù)
pool-3-thread-1正在執(zhí)行任務(wù)
pool-3-thread-3正在執(zhí)行任務(wù)
pool-3-thread-4正在執(zhí)行任務(wù)
pool-3-thread-1正在執(zhí)行任務(wù)
pool-3-thread-3正在執(zhí)行任務(wù)
pool-3-thread-4正在執(zhí)行任務(wù)
pool-3-thread-5正在執(zhí)行任務(wù)
pool-3-thread-4正在執(zhí)行任務(wù)
pool-3-thread-6正在執(zhí)行任務(wù)
pool-3-thread-7正在執(zhí)行任務(wù)
pool-3-thread-8正在執(zhí)行任務(wù)
pool-3-thread-9正在執(zhí)行任務(wù)
pool-3-thread-2正在執(zhí)行任務(wù)
pool-3-thread-6正在執(zhí)行任務(wù)
pool-3-thread-1正在執(zhí)行任務(wù)
pool-3-thread-3正在執(zhí)行任務(wù)
時(shí)間:1628926041159
時(shí)間:1628926041160
時(shí)間:1628926044172--pool-5-thread-1正在執(zhí)行任務(wù)
時(shí)間:1628926044172--pool-4-thread-2正在執(zhí)行任務(wù)
時(shí)間:1628926044172--pool-4-thread-1正在執(zhí)行任務(wù)
時(shí)間:1628926044172--pool-4-thread-2正在執(zhí)行任務(wù)
時(shí)間:1628926044172--pool-5-thread-1正在執(zhí)行任務(wù)
時(shí)間:1628926044172--pool-4-thread-2正在執(zhí)行任務(wù)
時(shí)間:1628926044172--pool-4-thread-1正在執(zhí)行任務(wù)
時(shí)間:1628926044172--pool-5-thread-1正在執(zhí)行任務(wù)
時(shí)間:1628926044172--pool-5-thread-1正在執(zhí)行任務(wù)
時(shí)間:1628926044172--pool-5-thread-1正在執(zhí)行任務(wù)
👏 new ThreadPoolExecutor() 自定義創(chuàng)建
public ThreadPoolExecutor(int corePoolSize,//線程池的核心線程數(shù)量
int maximumPoolSize,//線程池的最大線程數(shù)
long keepAliveTime,//當(dāng)線程數(shù)大于核心線程數(shù)時(shí),多余的空閑線程存活的最長(zhǎng)時(shí)間
TimeUnit unit,//時(shí)間單位
BlockingQueue<Runnable> workQueue,//任務(wù)隊(duì)列,用來(lái)儲(chǔ)存等待執(zhí)行任務(wù)的隊(duì)列
ThreadFactory threadFactory,//線程工廠,用來(lái)創(chuàng)建線程,一般默認(rèn)即可
RejectedExecutionHandler handler) //拒絕策略,當(dāng)提交的任務(wù)過(guò)多而不能及時(shí)處理時(shí),我們可以定制策略來(lái)處理任務(wù)
- corePoolSize:核心池的大小,這個(gè)參數(shù)跟后面講述的線程池的實(shí)現(xiàn)原理有非常大的關(guān)系。在創(chuàng)建了線程池后,默認(rèn)情況下,線程池中并沒(méi)有任何線程,而是等待有任務(wù)到來(lái)才創(chuàng)建線程去執(zhí)行任務(wù),除非調(diào)用了 prestartAllCoreThreads() 或者 prestartCoreThread() 方法,從這2個(gè)方法的名字就可以看出,是預(yù)創(chuàng)建線程的意思,即在沒(méi)有任務(wù)到來(lái)之前就創(chuàng)建 corePoolSize 個(gè)線程或者一個(gè)線程。默認(rèn)情況下,在創(chuàng)建了線程池后,線程池中的線程數(shù)為 0,當(dāng)有任務(wù)來(lái)之后,就會(huì)創(chuàng)建一個(gè)線程去執(zhí)行任務(wù),當(dāng)線程池中的線程數(shù)目達(dá)到 corePoolSize 后,就會(huì)把到達(dá)的任務(wù)放到緩存隊(duì)列當(dāng)中;
- maximumPoolSize:線程池最大線程數(shù),這個(gè)參數(shù)也是一個(gè)非常重要的參數(shù),它表示在線程池中最多能創(chuàng)建多少個(gè)線程;
- keepAliveTime:表示線程沒(méi)有任務(wù)執(zhí)行時(shí)最多保持多久時(shí)間會(huì)終止。默認(rèn)情況下,只有當(dāng)線程池中的線程數(shù)大于 corePoolSize 時(shí),keepAliveTime 才會(huì)起作用,直到線程池中的線程數(shù)不大于 corePoolSize,即當(dāng)線程池中的線程數(shù)大于 corePoolSize 時(shí),如果一個(gè)線程空閑的時(shí)間達(dá)到 keepAliveTime,則會(huì)終止,直到線程池中的線程數(shù)不超過(guò) corePoolSize。但是如果調(diào)用了 allowCoreThreadTimeOut(boolean) 方法,在線程池中的線程數(shù)不大于 corePoolSize 時(shí),keepAliveTime 參數(shù)也會(huì)起作用,直到線程池中的線程數(shù)為 0;
- unit:參數(shù) keepAliveTime 的時(shí)間單位,有7種取值,在 TimeUnit 類(lèi)中有7種靜態(tài)屬性,如下所示:
TimeUnit.DAYS; //天 TimeUnit.HOURS; //小時(shí) TimeUnit.MINUTES; //分鐘 TimeUnit.SECONDS; //秒 TimeUnit.MILLISECONDS; //毫秒 TimeUnit.MICROSECONDS; //微妙 TimeUnit.NANOSECONDS; //納秒
- workQueue:阻塞隊(duì)列。保存等待執(zhí)行的任務(wù)的阻塞隊(duì)列,當(dāng)提交一個(gè)新的任務(wù)到線程池以后, 線程池會(huì)根據(jù)當(dāng)前線程池中正在運(yùn)行著的線程的數(shù)量來(lái)決定對(duì)該任務(wù)的處理方式,主要有以下幾種處理方式:
- 直接切換:這種方式常用的隊(duì)列是 SynchronousQueue ,不進(jìn)行任務(wù)存儲(chǔ),直接執(zhí)行;
- 使用無(wú)界隊(duì)列:一般使用基于鏈表的阻塞隊(duì)列 LinkedBlockingQueue。如果使用這種方式,那么線程池中能夠創(chuàng)建的最大線程數(shù)就是 corePoolSize ,而 maximumPoolSize 就不會(huì)起作用了(因?yàn)槭菬o(wú)界的)。當(dāng)線程池中所有的核心線程都是 RUNNING狀態(tài) 時(shí),這時(shí)一個(gè)新的任務(wù)提交就會(huì)放入等待隊(duì)列中。
- 使用有界隊(duì)列:一般使用 ArrayBlockingQueue 。使用該方式可以將線程池的最大線程數(shù)量限制為 maximumPoolSize ,這樣能夠降低資源的消耗,但同時(shí)這種方式也使得線程池對(duì)線程的調(diào)度變得更困難,因?yàn)榫€程池和隊(duì)列的容量都是有限的值,所以要想使線程池處理任務(wù)的吞吐率達(dá)到一個(gè)相對(duì)合理的范圍,又想使線程調(diào)度相對(duì)簡(jiǎn)單,并且還要盡可能的降低線程池對(duì)資源的消耗,就需要合理的設(shè)置這兩個(gè)數(shù)量。
- PS:ArrayBlockingQueue 和 PriorityBlockingQueue 使用較少,一般使用 LinkedBlockingQueue 和 SynchronousQueue。線程池的排隊(duì)策略與 BlockingQueue 有關(guān)。
- threadFactory:用于設(shè)置創(chuàng)建線程的工廠,可以通過(guò)線程工廠給每個(gè)創(chuàng)建出來(lái)的線程做些更有意義的事情,比如設(shè)置 daemon 和 優(yōu)先級(jí) 等等
- handler:飽和策略。當(dāng)線程池的阻塞隊(duì)列已滿和指定的線程都已經(jīng)開(kāi)啟,說(shuō)明當(dāng)前線程池已經(jīng)處于飽和狀態(tài)了,那么就需要采用一種策略來(lái)處理這種情況。表示當(dāng)拒絕處理任務(wù)時(shí)的策略,有以下幾種取值(也可以根據(jù)應(yīng)用場(chǎng)景需要來(lái)實(shí)現(xiàn) RejectedExecutionHandler 接口自定義策略。如記錄日志或持久化不能處理的任務(wù)。):
AbortPolicy:默認(rèn)的拒絕策略,直接拋出異常。`throws RejectedExecutionException`。
CallerRunsPolicy:只用調(diào)用者所在線程來(lái)運(yùn)行任務(wù)(提交任務(wù)的線程自己去執(zhí)行該任務(wù))。
DiscardOldestPolicy:丟棄最老的任務(wù),其實(shí)就是把最早進(jìn)入工作隊(duì)列的任務(wù)丟棄,然后把新任務(wù)加入到工作隊(duì)列。
DiscardPolicy:不處理,直接丟棄任務(wù),沒(méi)有任何異常拋出。
執(zhí)行流程:
- 線程池創(chuàng)建線程,會(huì)判斷當(dāng)前線程數(shù)是否大于 corePoolSize。
- 如果大于則存在緩存隊(duì)列,緩沖隊(duì)列存滿后會(huì)繼續(xù)創(chuàng)建線程直到 maximumPoolSize ,拋出拒絕的異常。
- 如果小于則創(chuàng)建線程,執(zhí)行任務(wù),執(zhí)行完后會(huì)從緩存隊(duì)列中取任務(wù)再執(zhí)行
✨創(chuàng)建多少線程合適
一般多線程執(zhí)行的任務(wù)類(lèi)型可以分為 CPU 密集型 和 I/O 密集型,根據(jù)不同的任務(wù)類(lèi)型,我們計(jì)算線程數(shù)的方法也不一樣。創(chuàng)建多少線程合適,要看多線程具體的應(yīng)用場(chǎng)景。我們的程序一般都是 CPU 計(jì)算 和 I/O 操作交叉執(zhí)行 的,由于 I/O 設(shè)備 的速度相對(duì)于 CPU 來(lái)說(shuō)都很慢,所以大部分情況下,I/O 操作執(zhí)行的時(shí)間相對(duì)于 CPU 計(jì)算來(lái)說(shuō)都非常長(zhǎng) ,這種場(chǎng)景我們一般都稱(chēng)為 I/O 密集型計(jì)算 和 I/O 密集型計(jì)算 相對(duì)的就是 CPU 密集型計(jì)算,CPU 密集型計(jì)算 大部分場(chǎng)景下都是 純 CPU 計(jì)算。
- CPU 密集型任務(wù):多線程主要目的是提成CPU利用率,保持和CPU核數(shù)一致即可??梢詫⒕€程數(shù)設(shè)置為 N(CPU 核心數(shù))+1 ,比 CPU 核心數(shù) 多出來(lái)的一個(gè)線程是 為了防止線程偶發(fā)的缺頁(yè)中斷,或者其它原因?qū)е碌?任務(wù)暫停 而帶來(lái)的影響。一旦 任務(wù)暫停 ,CPU 就會(huì)處于 空閑狀態(tài) ,而在這種情況下多出來(lái)的一個(gè)線程就可以充分利用 CPU 的空閑時(shí)間。
- 測(cè)試代碼如下,結(jié)果還是自己親力親為吧實(shí)踐出真知
package com.base.demo.design.play;
import java.util.List;
/**
* @Description: CPU測(cè)試
* @BelongsProject: base-demo-design
* @BelongsPackage: com.base.demo.design.play
* @Author: ChenYongJia
* @CreateTime: 2021-08-14 16:13
* @Email: chen87647213@163.com
* @Version: 1.0
*/
public class CPUTypeTest implements Runnable {
/**
* 整體執(zhí)行時(shí)間,包括在隊(duì)列中等待的時(shí)間
*/
List<Long> wholeTimeList;
/**
* 真正執(zhí)行時(shí)間
*/
List<Long> runTimeList;
private long initStartTime = 0;
/**
* 構(gòu)造函數(shù)
*
* @param runTimeList
* @param wholeTimeList
* @return
* @date 2021/8/14 16:13
* @author ChenYongJia
* @version 1.0
*/
public CPUTypeTest(List<Long> runTimeList, List<Long> wholeTimeList) {
initStartTime = System.currentTimeMillis();
this.runTimeList = runTimeList;
this.wholeTimeList = wholeTimeList;
}
/**
* 判斷素?cái)?shù)
*
* @param number
* @return boolean
* @date 2021/8/14 16:13
* @author ChenYongJia
* @version 1.0
*/
public boolean isPrime(final int number) {
if (number <= 1)
return false;
for (int i = 2; i <= Math.sqrt(number); i++) {
if (number % i == 0)
return false;
}
return true;
}
/**
* 計(jì)算素?cái)?shù)
*
* @param lower
* @param upper
* @return int
* @date 2021/8/14 16:14
* @author ChenYongJia
* @version 1.0
*/
public int countPrimes(final int lower, final int upper) {
int total = 0;
for (int i = lower; i <= upper; i++) {
if (isPrime(i))
total++;
}
return total;
}
@Override
public void run() {
long start = System.currentTimeMillis();
countPrimes(1, 1000000);
long end = System.currentTimeMillis();
long wholeTime = end - initStartTime;
long runTime = end - start;
wholeTimeList.add(wholeTime);
runTimeList.add(runTime);
System.out.println("單個(gè)線程花費(fèi)時(shí)間:" + (end - start));
}
}
I/O 密集型任務(wù):這種任務(wù)應(yīng)用起來(lái),系統(tǒng)會(huì)用大部分的時(shí)間來(lái)處理 I/O 交互,而線程在處理 I/O 的時(shí)間段內(nèi)不會(huì)占用 CPU 來(lái)處理,這時(shí)就可以將 CPU 交出給其它線程使用。因此在 I/O 密集型任務(wù)的應(yīng)用中,我們可以多配置一些線程,具體的計(jì)算方法是 2N(CPU 核心數(shù))。
- 一般最佳線程數(shù)目 = (線程等待時(shí)間與線程CPU時(shí)間之比 + 1)* CPU數(shù)目
- 實(shí)戰(zhàn):實(shí)際需要根據(jù)上線情況進(jìn)行調(diào)整優(yōu)化
- 測(cè)試代碼如下,結(jié)果還是自己親力親為吧實(shí)踐出真知
package com.base.demo.design.play;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Vector;
/**
* @Description: IO測(cè)試
* @BelongsProject: base-demo-design
* @BelongsPackage: com.base.demo.design.play
* @Author: ChenYongJia
* @CreateTime: 2021-08-14 16:18
* @Email: chen87647213@163.com
* @Version: 1.0
*/
public class IOTypeTest implements Runnable {
/**
* 整體執(zhí)行時(shí)間,包括在隊(duì)列中等待的時(shí)間
*/
Vector<Long> wholeTimeList;
/**
* 真正執(zhí)行時(shí)間
*/
Vector<Long> runTimeList;
private long initStartTime = 0;
/**
* 構(gòu)造函數(shù)
*
* @param runTimeList
* @param wholeTimeList
*/
public IOTypeTest(Vector<Long> runTimeList, Vector<Long> wholeTimeList) {
initStartTime = System.currentTimeMillis();
this.runTimeList = runTimeList;
this.wholeTimeList = wholeTimeList;
}
/**
* IO操作
*
* @return void
* @date 2021/8/14 16:18
* @author ChenYongJia
* @version 1.0
*/
public void readAndWrite() throws IOException {
File sourceFile = new File("D:/test.txt");
//創(chuàng)建輸入流
BufferedReader input = new BufferedReader(new FileReader(sourceFile));
//讀取源文件,寫(xiě)入到新的文件
String line = null;
while ((line = input.readLine()) != null) {
//System.out.println(line);
}
//關(guān)閉輸入輸出流
input.close();
}
@Override
public void run() {
long start = System.currentTimeMillis();
try {
readAndWrite();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
long end = System.currentTimeMillis();
long wholeTime = end - initStartTime;
long runTime = end - start;
wholeTimeList.add(wholeTime);
runTimeList.add(runTime);
System.out.println("單個(gè)線程花費(fèi)時(shí)間:" + (end - start));
}
}
喜歡折騰的同學(xué)可以試試(我貼出來(lái)的兩段 TEST代碼):在不同線程數(shù)的情況下,run 方法運(yùn)行時(shí)間的差異??梢酝ㄟ^(guò)創(chuàng)建不同數(shù)量的線程,線程中 new 該 Test 對(duì)象(new 兩個(gè) List 傳到構(gòu)造參數(shù)里)并提交到線程池。查看并歸納計(jì)算得出結(jié)果。
附:線程池原理
學(xué)習(xí)線程池的實(shí)現(xiàn)原理,有助于你更好地理解內(nèi)容。
在 HotSpot VM 的線程模型中,Java 線程被一對(duì)一映射為內(nèi)核線程。Java 在使用線程執(zhí)行程序時(shí),需要?jiǎng)?chuàng)建一個(gè)內(nèi)核線程;當(dāng)該 Java 線程被終止時(shí),這個(gè)內(nèi)核線程也會(huì)被回收。因此 Java 線程的創(chuàng)建與銷(xiāo)毀將會(huì)消耗一定的計(jì)算機(jī)資源,從而增加系統(tǒng)的性能開(kāi)銷(xiāo)。
除此之外,大量創(chuàng)建線程同樣會(huì)給系統(tǒng)帶來(lái)性能問(wèn)題,因?yàn)閮?nèi)存和 CPU 資源都將被線程搶占,如果處理不當(dāng),就會(huì)發(fā)生內(nèi)存溢出、CPU 使用率超負(fù)荷等問(wèn)題。
為了解決上述兩類(lèi)問(wèn)題,Java 提供了線程池概念,對(duì)于頻繁創(chuàng)建線程的業(yè)務(wù)場(chǎng)景,線程池可以創(chuàng)建固定的線程數(shù)量,并且在操作系統(tǒng)底層,輕量級(jí)進(jìn)程將會(huì)把這些線程映射到內(nèi)核。
線程池可以提高線程復(fù)用,又可以固定最大線程使用量,防止無(wú)限制地創(chuàng)建線程。當(dāng)程序提交一個(gè)任務(wù)需要一個(gè)線程時(shí),會(huì)去線程池中查找是否有空閑的線程,若有,則直接使用線程池中的線程工作,若沒(méi)有,會(huì)去判斷當(dāng)前已創(chuàng)建的線程數(shù)量是否超過(guò)最大線程數(shù)量,如未超過(guò),則創(chuàng)建新線程,如已超過(guò),則進(jìn)行排隊(duì)等待或者直接拋出異常。
🎉最后
在不同的業(yè)務(wù)場(chǎng)景以及不同配置的部署機(jī)器中,線程池的線程數(shù)量設(shè)置是不一樣的。其設(shè)置不宜過(guò)大,也不宜過(guò)小,要根據(jù)具體情況,計(jì)算出一個(gè)大概的數(shù)值,再通過(guò)實(shí)際的性能測(cè)試,計(jì)算出一個(gè)合理的線程數(shù)量。
到此這篇關(guān)于Java線程池大小設(shè)置的文章就介紹到這了,更多相關(guān)Java線程池大小設(shè)置內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot添加自定義攔截器的實(shí)現(xiàn)代碼
這篇文章主要介紹了SpringBoot添加自定義攔截器的實(shí)現(xiàn)代碼,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-09-09
在Mybatis中association標(biāo)簽多層嵌套的問(wèn)題
這篇文章主要介紹了在Mybatis中association標(biāo)簽多層嵌套的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
Java異常鏈表throw結(jié)構(gòu)assert詳細(xì)解讀
這篇文章主要給大家介紹了關(guān)于Java中方法使用的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-08-08
Java?8?的異步編程利器?CompletableFuture的實(shí)例詳解
這篇文章主要介紹了Java?8?的異步編程利器?CompletableFuture?詳解,本文通過(guò)一個(gè)例子給大家介紹下Java?8??CompletableFuture異步編程的相關(guān)知識(shí),需要的朋友可以參考下2022-03-03
新手了解java 類(lèi),對(duì)象以及封裝基礎(chǔ)知識(shí)
JS是一門(mén)面向?qū)ο笳Z(yǔ)言,其對(duì)象是用prototype屬性來(lái)模擬的,本文介紹了如何封裝JS對(duì)象,具有一定的參考價(jià)值,下面跟著小編一起來(lái)看下吧,希望對(duì)你有所幫助2021-07-07
導(dǎo)出maven項(xiàng)目依賴(lài)的jar包(圖文教程)
下面小編就為大家?guī)?lái)一篇導(dǎo)出maven項(xiàng)目依賴(lài)的jar包(圖文教程)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10
vue3實(shí)現(xiàn)一個(gè)todo-list
這篇文章主要為大家詳細(xì)介紹了基于vuejs實(shí)現(xiàn)一個(gè)todolist項(xiàng)目,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能給你帶來(lái)幫助2021-08-08
Java回調(diào)函數(shù)原理實(shí)例與代理模式的區(qū)別講解
今天小編就為大家分享一篇關(guān)于Java回調(diào)函數(shù)原理實(shí)例與代理模式的區(qū)別講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-02-02

