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

java多線程文件下載器的實(shí)現(xiàn)

 更新時(shí)間:2023年11月10日 09:53:36   作者:是謝添啊  
本文主要介紹了java多線程文件下載器的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

1.簡介

該項(xiàng)目應(yīng)用的知識(shí)點(diǎn)包括:

  • RandomAccessFile 類的運(yùn)用
  • HttpURLConnection 類的運(yùn)用
  • 線程池的使用
  • 原子類 LongAdder 的運(yùn)用
  • CountDownLatch 類的運(yùn)用
  • ScheduledExecutorService 類的運(yùn)用

2.文件下載的核心

從互聯(lián)網(wǎng)下載文件有點(diǎn)類似于我們將本地某個(gè)文件復(fù)制到另一個(gè)目錄下,也會(huì)利用 IO 流進(jìn)行操作。對(duì)于從互聯(lián)網(wǎng)下載,還需要將本地和下載文件所在的服務(wù)器建立連接。

3.文件下載器的基礎(chǔ)代碼

3.1 HttpURLConnection

從互聯(lián)網(wǎng)中下載文件的話,需要與文件所在的服務(wù)器建立連接,這里可以使用 jdk 提供的 java.net.HttpURLConnection 類來幫助我們完成這個(gè)操作。jdk11中有提供 java.net.http.HttpClient 類來替代 HttpURLConnection,由于現(xiàn)在使用的是 jdk8,因此先不用 jdk11 中的 HttpClient。除此之外還有一些其他第三方提供類可以執(zhí)行類似的操作,這里就不贅述了。

3.2 用戶標(biāo)識(shí)

我們通過瀏覽器訪問某個(gè)網(wǎng)站的時(shí)候,會(huì)將當(dāng)前瀏覽器的版本,操作系統(tǒng)版本等信息的標(biāo)識(shí)發(fā)送到網(wǎng)站所在的服務(wù)器中。當(dāng)用程序代碼去訪問網(wǎng)站時(shí),需要將這個(gè)標(biāo)識(shí)發(fā)送過去。

Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1

4.下載信息

4.1 計(jì)劃任務(wù)

文件下載的時(shí)候最好能夠展示出下載的速度,已下載文件大小等信息。這里可以每隔一段時(shí)間來獲取文件的下載信息,比如間隔 1 秒獲取一次,然后將信息打印到控制臺(tái)。文件下載是一個(gè)獨(dú)立的線程,另外還需要再開啟一個(gè)線程來間隔獲取文件的信息。java.util.concurrent.ScheduledExecutorService 這個(gè)類可以幫助我們來實(shí)現(xiàn)此功能。

4.2 ScheduledExecutorService

在該類中提供了一些方法可以幫助開發(fā)者實(shí)現(xiàn)間隔執(zhí)行的效果,下面列出一些常見的方法及其參數(shù)說明。我們可以通過下面方式來獲取該類的對(duì)象,其中 1 標(biāo)識(shí)核心線程的數(shù)量。

ScheduledExecutorService s = Executors.newScheduledThreadPool(1);

?? schedule方法

該方法是重載的,這兩個(gè)重載的方法都是有 3 個(gè)形參,只是第一個(gè)形參不同。

參數(shù)含義
Runnable / Callable<V>可以傳入這兩個(gè)類型的任務(wù)
long delay延時(shí)的時(shí)間數(shù)量
TimeUnit unit時(shí)間單位

該方法的作用是讓任務(wù)按照指定的時(shí)間延時(shí)執(zhí)行。

?? scheduleAtFixedRate方法

該方法的作用是按照指定的時(shí)間延時(shí)執(zhí)行,并且每隔一段時(shí)間再繼續(xù)執(zhí)行。

參數(shù)含義
Runnable command執(zhí)行的任務(wù)
long initialDelay延時(shí)的時(shí)間數(shù)量
long period間隔的時(shí)間數(shù)量
TimeUnit unit時(shí)間單位

倘若在執(zhí)行任務(wù)的時(shí)候,耗時(shí)超過了間隔時(shí)間,則任務(wù)執(zhí)行結(jié)束之后直接再次執(zhí)行,而不是再等待間隔時(shí)間執(zhí)行。

?? scheduleWithFixedDelay方法

該方法的作用是按照指定的時(shí)間延時(shí)執(zhí)行,并且每隔一段時(shí)間再繼續(xù)執(zhí)行。

參數(shù)含義
Runnable command執(zhí)行的任務(wù)
long initialDelay延時(shí)的時(shí)間數(shù)量
long period間隔的時(shí)間數(shù)量
TimeUnit unit時(shí)間單位

在執(zhí)行任務(wù)的時(shí)候,無論耗時(shí)多久,任務(wù)執(zhí)行結(jié)束之后都會(huì)等待間隔時(shí)間之后再繼續(xù)下次任務(wù)。

5.線程池簡介

線程在創(chuàng)建,銷毀的過程中會(huì)消耗一些資源,為了節(jié)省這些開銷,jdk 添加了線程池。線程池節(jié)省了開銷,提高了線程使用的效率。阿里巴巴開發(fā)文檔中建議在編寫多線程程序的時(shí)候使用線程池。

5.1 ThreadPoolExecutor 構(gòu)造方法參數(shù)

在 juc 包下提供了 ThreadPoolExecutor 類,可以通過該類來創(chuàng)建線程池,這個(gè)類中有4個(gè)重載的構(gòu)造方法,最核心的構(gòu)造方法是有7個(gè)形參的,這些參數(shù)所代表的意義如下:

參數(shù)含義
corePoolSize線程池中核心線程的數(shù)量
maximumPoolSize線程池中最大線程的數(shù)量,是核心線程數(shù)量和非核心線程數(shù)量之和
keepAliveTime非核心線程空閑的生存時(shí)間
unitkeepAliveTime 的生存時(shí)間單位
workQueue當(dāng)沒有空閑的線程時(shí),新的任務(wù)會(huì)加入到 workQueue 中排隊(duì)等待
threadFactory線程工廠,用于創(chuàng)建線程
handler拒絕策略,當(dāng)任務(wù)太多無法處理時(shí)的拒絕策略

5.2 線程池工作過程

5.3 線程池的狀態(tài)

狀態(tài)說明
RUNNING創(chuàng)建線程池之后的狀態(tài)是 RUNNING
SHUTDOWN該狀態(tài)下,線程池就不會(huì)接收新任務(wù),但會(huì)處理阻塞隊(duì)列剩余任務(wù),相對(duì)溫和
STOP該狀態(tài)下會(huì)中斷正在執(zhí)行的任務(wù),并拋棄阻塞隊(duì)列任務(wù),相對(duì)暴力
TIDYING任務(wù)全部執(zhí)行完畢,活動(dòng)線程為 0 即將進(jìn)入終止
TERMINATED線程池終止

5.4 線程池的關(guān)閉

線程池使用完畢之后需要進(jìn)行關(guān)閉,提供了以下兩種方法進(jìn)行關(guān)閉。

方法說明
shutdown()該方法執(zhí)行后,線程池狀態(tài)變?yōu)?SHUTDOWN,不會(huì)接收新任務(wù),但是會(huì)執(zhí)行完已提交的任務(wù),此方法不會(huì)阻塞調(diào)用線程的執(zhí)行。
shutdownNow()該方法執(zhí)行后,線程池狀態(tài)變?yōu)?STOP,不會(huì)接收新任務(wù),會(huì)將隊(duì)列中的任務(wù)返回,并用 interrupt 的方式中斷正在執(zhí)行的任務(wù)。

5.5 工作隊(duì)列

jdk 中提供的一些工作隊(duì)列 workQueue。

隊(duì)列說明
SynchronousQueue直接提交隊(duì)列
ArrayBlockingQueue有界隊(duì)列,可以指定容量
LinkedBlockingDeque無界隊(duì)列
PriorityBlockingQueue優(yōu)先任務(wù)隊(duì)列,可以根據(jù)任務(wù)優(yōu)先級(jí)順序執(zhí)行任務(wù)

6.代碼實(shí)現(xiàn)

6.1 環(huán)境搭建

?? 基本信息

  • 開發(fā)工具:IDEA
  • JDK 版本:8
  • 項(xiàng)目編碼:utf-8

?? 創(chuàng)建項(xiàng)目

在開發(fā)工具中創(chuàng)建一個(gè) javase 項(xiàng)目即可,無需導(dǎo)入第三方 jar 依賴。

6.2 實(shí)現(xiàn)邏輯

  • 先判斷是否已存在重復(fù)文件,該步驟其實(shí)可忽略,因?yàn)樽罱K下載合并的文件名已采用時(shí)間戳進(jìn)行了唯一標(biāo)識(shí);
  • 啟動(dòng)一個(gè)線程每隔一秒打印下載情況;
  • 切分任務(wù),多線程分快下載;
  • 全部塊文件下載完畢,合并分塊文件;
  • 合并分塊文件完畢,清理分塊文件;
  • 釋放資源,關(guān)閉線程池和連接對(duì)象。

6.3 項(xiàng)目結(jié)構(gòu)

包名作用
constant存放常量類的包
core存放了下載器核心類的包
util存放工具類的包
Main主類

6.4 類代碼

?? constant 包

?? Constant

/**
 * Description: 存放項(xiàng)目常量
 *
 * @Author 狐貍半面添
 * @Create 2023/11/6 1:22
 * @Version 1.0
 */
public class Constant {
    /**
     * 指定下載目錄的存放位置
     */
    public static final String PATH = "D:\\download\\";

    public static final double MB = 1024d * 1024d;
    public static final double KB = 1024d;

    /**
     * 每次讀取的字節(jié)大小
     */
    public static final int BYTE_SIZE = 1024 * 100;

    /**
     * 塊文件(臨時(shí)文件)的后綴
     */
    public static final String PART_FILE_SUFFIX = ".temp";

    /**
     * 線程數(shù)量
     */
    public static final int THREAD_NUM = 5;

    // 創(chuàng)建存放位置的代碼
    // public static void main(String[] args) {
    //     File file = new File("D:\\download");
    //     if (!file.exists()) {
    //         file.mkdir();
    //     }
    // }
}

?? util 包

?? FileUtils

/**
 * Description: 文件相關(guān)工具
 *
 * @Author 狐貍半面添
 * @Create 2023/11/6 11:46
 * @Version 1.0
 */
public class FileUtils {
    /**
     * 獲取本地文件的大小
     *
     * @param path 文件路徑
     * @return 文件大小
     */
    public static long getFileContentLength(String path) {
        File file = new File(path);
        return file.exists() && file.isFile() ? file.length() : 0;
    }
}

?? HttpUtils

/**
 * Description: Http 相關(guān)工具類
 *
 * @Author 狐貍半面添
 * @Create 2023/11/6 1:06
 * @Version 1.0
 */
public class HttpUtils {

    private static long id = System.currentTimeMillis();

    public static void change() {
        id = System.currentTimeMillis();
    }

    /**
     * 獲取下載的文件大小
     *
     * @param url 下載文件鏈接
     * @return 文件大小
     * @throws IOException
     */
    public static long getHttpFileContentLength(String url) throws IOException {
        int contentLength;
        HttpURLConnection httpURLConnection = null;
        try {
            httpURLConnection = getHttpURLConnection(url);
            contentLength = httpURLConnection.getContentLength();
        } finally {
            if (httpURLConnection != null) {
                httpURLConnection.disconnect();
            }
        }
        return contentLength;
    }

    /**
     * 分塊下載
     *
     * @param url      下載地址
     * @param startPos 下載文件起始位置
     * @param endPos   下載文件結(jié)束位置
     * @return 連接對(duì)象
     */
    public static HttpURLConnection getHttpURLConnection(String url, long startPos, long endPos) throws IOException {
        HttpURLConnection httpURLConnection = getHttpURLConnection(url);
        LogUtils.info("下載的區(qū)間是:{}-{}", startPos, endPos);

        if (endPos != 0) {
            httpURLConnection.setRequestProperty("RANGE", "bytes=" + startPos + "-" + endPos);
        } else {
            httpURLConnection.setRequestProperty("RANGE", "bytes=" + startPos + "-");
        }

        return httpURLConnection;
    }

    /**
     * 獲取 HttpURLConnection 連接對(duì)象
     *
     * @param url 文件的地址
     * @return HttpURLConnection 連接對(duì)象
     */
    public static HttpURLConnection getHttpURLConnection(String url) throws IOException {
        URL httpUrl = new URL(url);
        HttpURLConnection httpURLConnection = (HttpURLConnection) httpUrl.openConnection();
        // 向文件所在的服務(wù)器發(fā)送標(biāo)識(shí)信息
        httpURLConnection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1");
        return httpURLConnection;
    }

    /**
     * 獲取下載文件的名字
     *
     * @param url 下載地址
     * @return 文件名
     */
    public static String getHttpFileName(String url) {

        String fileName;

        int startIndex = url.lastIndexOf("/");
        int endIndex = url.lastIndexOf("?");
        if (endIndex == -1) {
            fileName = url.substring(startIndex + 1);
        } else {
            fileName = url.substring(startIndex + 1, endIndex);
        }

        int pointIndex = fileName.lastIndexOf(".");

        return fileName.substring(0, fileName.lastIndexOf(".")) + "-" + id + fileName.substring(pointIndex);
    }

}

?? LogUtils

/**
 * Description: 日志工具類
 *
 * @Author 狐貍半面添
 * @Create 2023/11/6 1:41
 * @Version 1.0
 */
public class LogUtils {
    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("hh:mm:ss");

    public static void info(String msg, Object... args) {
        print(msg, "-info-", args);
    }

    public static void error(String msg, Object... args) {
        print(msg, "-error-", args);
    }

    private static void print(String msg, String level, Object... args) {
        if (args != null && args.length > 0) {
            msg = String.format(msg.replace("{}", "%s"), args);
        }
        String threadName = Thread.currentThread().getName();
        System.out.println(LocalTime.now().format(FORMATTER) + "  " + threadName + level + msg);
    }
}

?? core 包

?? DownloadInfoThread

/**
 * Description: 展示下載信息
 *
 * @Author 狐貍半面添
 * @Create 2023/11/6 2:07
 * @Version 1.0
 */
@SuppressWarnings("AlibabaUndefineMagicConstant")
public class DownloadInfoThread implements Runnable {
    /**
     * 下載文件總大小
     */
    private final long httpFileContentLength;


    /**
     * 本次累計(jì)下載的大小
     */
    public static volatile LongAdder downSize = new LongAdder();

    /**
     * 前一次下載的大小
     */
    public double prevSize;

    public DownloadInfoThread(long httpFileContentLength) {
        this.httpFileContentLength = httpFileContentLength;
    }

    @Override
    public void run() {
        // 計(jì)算文件總大小 單位是 MB
        String httpFileSize = String.format("%.2f", httpFileContentLength / Constant.MB);

        // 計(jì)算每秒下載速度 kb
        int speed = (int) ((downSize.doubleValue() - prevSize) / Constant.KB);

        prevSize = downSize.doubleValue();

        // 剩余文件的大小
        double remainSize = httpFileContentLength - downSize.doubleValue();

        // 計(jì)算剩余時(shí)間
        String remainTime = String.format("%.1f", remainSize / Constant.KB / speed);

        if ("Infinity".equalsIgnoreCase(remainTime)) {
            remainTime = "-";
        }

        // 已下載大小
        String currentFileSize = String.format("%.1f", downSize.doubleValue() / Constant.MB);

        String speedInfo = String.format("已下載 %smb/%smb,速度 %skb/s,剩余時(shí)間 %ss", currentFileSize, httpFileSize, speed, remainTime);

        System.out.print("\r");
        System.out.print(speedInfo);

    }
}

?? DownloaderTask

/**
 * Description: 分塊下載任務(wù)
 *
 * @Author 狐貍半面添
 * @Create 2023/11/7 0:58
 * @Version 1.0
 */
public class DownloaderTask implements Callable<Boolean> {

    private final String url;

    /**
     * 下載起始位置
     */
    private final long startPos;

    /**
     * 下載結(jié)束位置
     */
    private final long endPos;

    /**
     * 標(biāo)識(shí)當(dāng)前是哪一部分
     */
    private final int part;

    private final CountDownLatch countDownLatch;

    public DownloaderTask(String url, long startPos, long endPos, int part, CountDownLatch countDownLatch) {
        this.url = url;
        this.startPos = startPos;
        this.endPos = endPos;
        this.part = part;
        this.countDownLatch = countDownLatch;
    }

    @Override
    public Boolean call() throws Exception {
        // 獲取文件名
        String httpFileName = HttpUtils.getHttpFileName(url);
        // 分塊的文件名
        httpFileName = httpFileName + Constant.PART_FILE_SUFFIX + part;
        // 下載路徑
        httpFileName = Constant.PATH + httpFileName;

        // 獲取分塊下載的連接
        HttpURLConnection httpURLConnection = HttpUtils.getHttpURLConnection(url, startPos, endPos);

        try (
                InputStream input = httpURLConnection.getInputStream();
                BufferedInputStream bis = new BufferedInputStream(input);
                RandomAccessFile accessFile = new RandomAccessFile(httpFileName, "rw");
        ) {
            byte[] buffer = new byte[Constant.BYTE_SIZE];
            int len;
            // 循環(huán)讀取數(shù)據(jù)
            while ((len = bis.read(buffer)) != -1) {
                // 1s 內(nèi)下載的數(shù)據(jù),通過原子類下載
                DownloadInfoThread.downSize.add(len);
                accessFile.write(buffer, 0, len);
            }
        } catch (FileNotFoundException e) {
            LogUtils.error("下載文件不存在 {}", url);
            return false;
        } catch (Exception e) {
            LogUtils.error("下載出現(xiàn)異常");
            return false;
        } finally {
            httpURLConnection.disconnect();
            countDownLatch.countDown();
        }

        return true;
    }

}

?? Downloader

/**
 * Description: 下載器
 *
 * @Author 狐貍半面添
 * @Create 2023/11/6 1:21
 * @Version 1.0
 */
public class Downloader {

    private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);

    public ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(Constant.THREAD_NUM,
            Constant.THREAD_NUM,
            0,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(5));

    private CountDownLatch countDownLatch = new CountDownLatch(Constant.THREAD_NUM);

    public void download(String url) {
        // 獲取文件名
        String httpFileName = HttpUtils.getHttpFileName(url);
        // 文件下載路徑
        httpFileName = Constant.PATH + httpFileName;
        // 獲取本地文件的大小
        long localFileLength = FileUtils.getFileContentLength(httpFileName);


        HttpURLConnection httpURLConnection = null;
        DownloadInfoThread downloadInfoThread;
        try {
            // 獲取連接對(duì)象
            httpURLConnection = HttpUtils.getHttpURLConnection(url);

            // 獲取下載文件的總大小
            int contentLength = httpURLConnection.getContentLength();

            // 判斷文件是否已下載過
            if (localFileLength >= contentLength) {
                LogUtils.info("{} 已下載完畢,無需重新下載", httpFileName);
                // 關(guān)閉連接對(duì)象
                httpURLConnection.disconnect();
                // 關(guān)閉線程池
                scheduledExecutorService.shutdownNow();
                poolExecutor.shutdown();

                return;
            }

            // 創(chuàng)建獲取下載信息的任務(wù)對(duì)象
            downloadInfoThread = new DownloadInfoThread(contentLength);

            // 將任務(wù)交給線程執(zhí)行,每隔 1s 打印一次
            scheduledExecutorService.scheduleAtFixedRate(downloadInfoThread, 1, 1, TimeUnit.SECONDS);

            // 切分任務(wù)
            ArrayList<Future> list = new ArrayList<>();
            split(url, list);

            countDownLatch.await();

            System.out.print("\r");
            System.out.println("分塊文件下載完成");

            // 合并文件
            if (merge(httpFileName)) {
                // 清除臨時(shí)文件
                clearTemp(httpFileName);
            }


        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("本次執(zhí)行完成");

            // 關(guān)閉連接對(duì)象
            if (httpURLConnection != null) {
                httpURLConnection.disconnect();
            }

            // 關(guān)閉線程池
            scheduledExecutorService.shutdownNow();
            poolExecutor.shutdown();
        }
    }

    /**
     * 文件切分
     *
     * @param url        文件鏈接
     * @param futureList 任務(wù)集合
     */
    public void split(String url, ArrayList<Future> futureList) {
        try {
            // 獲取下載文件大小
            long contentLength = HttpUtils.getHttpFileContentLength(url);

            // 計(jì)算切分后的文件大小
            long size = contentLength / Constant.THREAD_NUM;

            // 計(jì)算分塊個(gè)數(shù)
            for (int i = 0; i < Constant.THREAD_NUM; i++) {
                // 計(jì)算下載起始位置
                long startPos = i * size;

                // 計(jì)算結(jié)束位置
                long endPos;
                if (i == Constant.THREAD_NUM - 1) {
                    // 下載最后一塊
                    endPos = 0;
                } else {
                    endPos = startPos + size - 1;
                }

                // 創(chuàng)建任務(wù)對(duì)象
                DownloaderTask downloaderTask = new DownloaderTask(url, startPos, endPos, i, countDownLatch);
                // 將任務(wù)提交到線程池
                Future<Boolean> future = poolExecutor.submit(downloaderTask);

                futureList.add(future);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 文件合并
     *
     * @param fileName 文件名
     * @return 是否合并成功
     */
    public boolean merge(String fileName) {
        LogUtils.info("開始合并文件 {}", fileName);
        byte[] buffer = new byte[Constant.BYTE_SIZE];
        int len;
        try (
                RandomAccessFile accessFile = new RandomAccessFile(fileName, "rw")
        ) {
            for (int i = 0; i < Constant.THREAD_NUM; i++) {
                try (
                        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(fileName + Constant.PART_FILE_SUFFIX + i))
                ) {
                    while ((len = bis.read(buffer)) != -1) {
                        accessFile.write(buffer, 0, len);
                    }

                }
            }

            LogUtils.info("文件合并完畢 {}", fileName);

        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

        return true;
    }

    /**
     * 清除臨時(shí)文件
     *
     * @param fileName 文件名
     */
    public void clearTemp(String fileName) {
        LogUtils.info("清理分塊文件");
        for (int i = 0; i < Constant.THREAD_NUM; i++) {
            String name = fileName + Constant.PART_FILE_SUFFIX + i;
            File file = new File(name);
            file.delete();
        }
        LogUtils.info("分塊清除完畢");
    }
}

?? Main 主類

public class Main {
    public static void main(String[] args) {
        // 下載地址
        String url = null;

        if (args == null || args.length == 0) {
            while (url == null || url.trim().isEmpty()) {
                System.out.print("請(qǐng)輸入下載鏈接:");
                Scanner scanner = new Scanner(System.in);
                url = scanner.next();
            }
        } else {
            url = args[0];
        }

        Downloader downloader = new Downloader();
        downloader.download(url);

    }
}

到此這篇關(guān)于java多線程文件下載器的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)java多線程文件下載器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java的LinkedHashMap的實(shí)現(xiàn)原理詳解

    Java的LinkedHashMap的實(shí)現(xiàn)原理詳解

    這篇文章主要介紹了Java的LinkedHashMap的實(shí)現(xiàn)原理詳解,???LinkedHashMap是Map接口的哈希表和鏈接列表實(shí)現(xiàn),具有可預(yù)知的迭代順序,此實(shí)現(xiàn)提供所有可選的映射操作,并允許使用null值和null鍵,此類不保證映射的順序,特別是它不保證該順序恒久不變,需要的朋友可以參考下
    2023-09-09
  • Java多線程同步工具類CyclicBarrier的使用

    Java多線程同步工具類CyclicBarrier的使用

    本文主要介紹了Java多線程同步工具類CyclicBarrier的使用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • 利用Spring Cloud Config結(jié)合Bus實(shí)現(xiàn)分布式配置中心的步驟

    利用Spring Cloud Config結(jié)合Bus實(shí)現(xiàn)分布式配置中心的步驟

    這篇文章主要介紹了利用Spring Cloud Config結(jié)合Bus實(shí)現(xiàn)分布式配置中心的相關(guān)資料,文中通過示例代碼將實(shí)現(xiàn)的步驟一步步介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友下面來一起看看吧
    2018-05-05
  • springboot+mybatis-plus基于攔截器實(shí)現(xiàn)分表的示例代碼

    springboot+mybatis-plus基于攔截器實(shí)現(xiàn)分表的示例代碼

    本文主要介紹了springboot+mybatis-plus基于攔截器實(shí)現(xiàn)分表,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • IDEA之啟動(dòng)參數(shù),配置文件默認(rèn)參數(shù)的操作

    IDEA之啟動(dòng)參數(shù),配置文件默認(rèn)參數(shù)的操作

    這篇文章主要介紹了IDEA之啟動(dòng)參數(shù),配置文件默認(rèn)參數(shù)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-01-01
  • Maven配置中repositories、distributionManagement、pluginRepositories用法及將已有jar包部署到私服

    Maven配置中repositories、distributionManagement、pluginRepositori

    這篇文章主要介紹了Maven配置中repositories、distributionManagement、pluginRepositories用法及將已有jar包部署到私服,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • Java?Web中ServletContext對(duì)象詳解與應(yīng)用

    Java?Web中ServletContext對(duì)象詳解與應(yīng)用

    ServletContext是一個(gè)容器,可以用來存放變量,供一個(gè)web項(xiàng)目中多個(gè)Servlet共享,下面這篇文章主要給大家介紹了關(guān)于Java?Web中ServletContext對(duì)象詳解與應(yīng)用的相關(guān)資料,需要的朋友可以參考下
    2023-04-04
  • 解決在啟動(dòng)eclipse的tomcat進(jìn)行訪問時(shí)出現(xiàn)404問題的方法

    解決在啟動(dòng)eclipse的tomcat進(jìn)行訪問時(shí)出現(xiàn)404問題的方法

    這篇文章主要介紹了解決在啟動(dòng)eclipse的tomcat進(jìn)行訪問時(shí)出現(xiàn)404問題的方法,感興趣的小伙伴們可以參考一下
    2016-04-04
  • Java正則表達(dá)式之全量匹配和部分匹配

    Java正則表達(dá)式之全量匹配和部分匹配

    正則表達(dá)式異常強(qiáng)大,一直理解不深,用的也不深,這次項(xiàng)目中嘗試,體會(huì)到了它的強(qiáng)大之處,這篇文章主要給大家介紹了關(guān)于Java正則表達(dá)式之全量匹配和部分匹配的相關(guān)資料,需要的朋友可以參考下
    2023-06-06
  • 教你用Java驗(yàn)證服務(wù)器登錄系統(tǒng)

    教你用Java驗(yàn)證服務(wù)器登錄系統(tǒng)

    這篇文章主要介紹了教你用Java驗(yàn)證服務(wù)器登錄系統(tǒng),文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有很好的幫助,需要的朋友可以參考下
    2021-04-04

最新評(píng)論