最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java并發(fā)線程池實(shí)例分析講解

 更新時(shí)間:2023年02月02日 14:07:35   作者:飛奔的小付  
這篇文章主要介紹了Java并發(fā)線程池實(shí)例,線程池——控制線程創(chuàng)建、釋放,并通過(guò)某種策略嘗試復(fù)用線程去執(zhí)行任務(wù)的一個(gè)管理框架,從而實(shí)現(xiàn)線程資源與任務(wù)之間一種平衡

一.為什么要用線程池

先來(lái)看個(gè)簡(jiǎn)單的例子

1.直接new Thread的情況:

   public static void main(String[] args) throws InterruptedException {
        long start = System.currentTimeMillis();
        final List<Integer> list = new ArrayList<>();
        final Random random = new Random();
        for (int i = 0; i < 100000; i++) {
            Thread thread = new Thread() {
                @Override
                public void run() {
                    list.add(random.nextInt());
                }
            };
            thread.start();
            thread.join();
        }
        System.out.println("執(zhí)行時(shí)間:" + (System.currentTimeMillis() - start));
        System.out.println("執(zhí)行大小:" + list.size());
    }

執(zhí)行時(shí)間:6437

執(zhí)行大?。?00000

2.使用線程池時(shí)

  public static void main(String[] args) throws InterruptedException {
        long start = System.currentTimeMillis();
        final List<Integer> list = new ArrayList<>();
        final Random random = new Random();
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 100000; i++) {
            executorService.execute(()->{
                list.add(random.nextInt());
            });
        }
        executorService.shutdown();
        executorService.awaitTermination(1, TimeUnit.DAYS);
        System.out.println("執(zhí)行時(shí)間:" + (System.currentTimeMillis() - start));
        System.out.println("執(zhí)行大小:" + list.size());
    }

執(zhí)行時(shí)間:82

執(zhí)行大?。?00000

從執(zhí)行時(shí)間可以看出來(lái),使用線程池的效率要遠(yuǎn)遠(yuǎn)超過(guò)直接new Thread。

二.線程池的好處

  • 降低資源消耗。通過(guò)重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗。
  • 提高響應(yīng)速度。當(dāng)任務(wù)到達(dá)時(shí),任務(wù)可以不需要的等到線程創(chuàng)建就能立即執(zhí)行。
  • 提高線程的可管理性。線程是稀缺資源,如果無(wú)限制的創(chuàng)建,不僅會(huì)消耗系統(tǒng)資源,還會(huì)降低系統(tǒng)的穩(wěn)定性,使用線程池可以進(jìn)行統(tǒng)一的分配,調(diào)優(yōu)和監(jiān)控。

三.原理解析

四.4種線程池

1.newCachedThreadPool

  public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

特點(diǎn):newCachedThreadPool會(huì)創(chuàng)建一個(gè)可緩存線程池,如果當(dāng)前線程池的長(zhǎng)度超過(guò)了處理的需要時(shí),可以靈活的回收空閑的線程,當(dāng)需要增加時(shí),它可以靈活的添加新的線程,而不會(huì)對(duì)線程池的長(zhǎng)度作任何限制。

因?yàn)槠渥畲缶€程數(shù)是Integer.MAX_VALUE,若新建的線程數(shù)多了,會(huì)超過(guò)機(jī)器的可用內(nèi)存而OOM,但是因?yàn)槠洳皇菬o(wú)界隊(duì)列,所以在OOM之前一般會(huì)CPU 100%。

2.newFixedThreadPool

 public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

該方法會(huì)創(chuàng)建一個(gè)固定長(zhǎng)度的線程池,控制最大并發(fā)數(shù),超出的線程會(huì)在隊(duì)列中等待,因?yàn)榫€程的數(shù)量是固定的,但是阻塞隊(duì)列是無(wú)界的,如果請(qǐng)求數(shù)較多時(shí),會(huì)造成阻塞隊(duì)列越來(lái)越長(zhǎng),超出可用內(nèi)存 進(jìn)而OOM,所以要根據(jù)系統(tǒng)資源設(shè)置線程池的大小。Runtime.getRuntime().availableProcessors()

3.newSingleThreadExecutor

 public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

會(huì)創(chuàng)建一個(gè)單一的線程,前一個(gè)任務(wù)執(zhí)行完畢才會(huì)執(zhí)行下一個(gè)線程,F(xiàn)IFO,保證順序執(zhí)行。但是高并發(fā)下不太適用

4.newScheduledThreadPool

public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

創(chuàng)建一個(gè)固定長(zhǎng)度的線程池,而且支持定時(shí)的以及周期性的任務(wù)執(zhí)行,所有任務(wù)都是串行執(zhí)行的,同一時(shí)間只能有一個(gè)任務(wù)在執(zhí)行,前一個(gè)任務(wù)的延遲或異常都將會(huì)影響到之后的任務(wù)。

阿里規(guī)范中不推薦使用以上線程池,推薦使用自定義的線程池,當(dāng)然如果你的項(xiàng)目中的數(shù)量級(jí)比較小的話那到?jīng)]什么影響。

自定義線程池:

 ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20,
                0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(10),new MonkeyRejectedExecutionHandler());

執(zhí)行優(yōu)先級(jí) : 核心線程>非核心線程>隊(duì)列

提交優(yōu)先級(jí) : 核心線程>隊(duì)列>非核心線程

五.線程池處理流程

流程圖:

六.源碼分析

流程圖

ThreadPoolExecutor的execute方法

public void execute(Runnable command) {
	if (command == null)
            throw new NullPointerException();
 	int c = ctl.get();
 	//1.判斷線程數(shù)是否小于核心線程數(shù),如果是則使用入?yún)⑷蝿?wù)通過(guò)addWorker方法創(chuàng)建一個(gè)新的線程,如果能完成新線程創(chuàng)建execute方法結(jié)束,成功提交任務(wù)
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    //2.在第一步?jīng)]有完成任務(wù)提交;狀態(tài)為運(yùn)行并且能成功加入任務(wù)到工作隊(duì)列后,再進(jìn)行一次check,如果狀態(tài)在任務(wù)加入隊(duì)列后變?yōu)榱朔沁\(yùn)行(有可能是在執(zhí)行到這里線程池shtdown了),非運(yùn)行狀態(tài)下當(dāng)然是需要reject;
    // offer和add方法差不多,add方法就是調(diào)用的offer,只不過(guò)比offer多拋出一個(gè)異常 throw new IllegalStateException("Queue full")
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        if (! isRunning(recheck) && remove(command))
            reject(command);
            //3.判斷當(dāng)前工作線程池?cái)?shù)是否為0,如果是創(chuàng)建一個(gè)null任務(wù),任務(wù)在堵塞隊(duì)列存在了就會(huì)從隊(duì)列中取出這樣做的意義是保證線程池在running狀態(tài)必須有一個(gè)任務(wù)在執(zhí)行
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    //4.如果不能加入任務(wù)到工作隊(duì)列,將嘗試使用任務(wù)新增一個(gè)線程,如果失敗,則是線程池已經(jīng)shutdown或者線程池已經(jīng)達(dá)到飽和狀態(tài),所以reject.拒絕策略不僅僅是在飽和狀態(tài)下使用,在線程池進(jìn)入到關(guān)閉階段同樣需要使用到;
    else if (!addWorker(command, false))
        reject(command);
 	}
}

再進(jìn)入到addWork方法

private boolean addWorker(Runnable firstTask, boolean core) {
		// goto寫(xiě)法 重試
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);
            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                   //線程狀態(tài)非運(yùn)行并且非shutdown狀態(tài)任務(wù)為空,隊(duì)列非空就不能新增線程了
                return false;
            for (;;) {
                int wc = workerCountOf(c);
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    //當(dāng)前線程達(dá)到了最大閾值 就不再新增線程了
                    return false;
                if (compareAndIncrementWorkerCount(c))
                	//ctl+1工作線程池?cái)?shù)量+1如果成功 就跳出死循環(huán)
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                //進(jìn)來(lái)的狀態(tài)和此時(shí)的狀態(tài)發(fā)生改變重頭開(kāi)始重試
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            w = new Worker(firstTask);//內(nèi)部類封裝了線程和任務(wù) 通過(guò)threadfactory創(chuàng)建線程
            //毎一個(gè)worker就是一個(gè)線程數(shù)
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                   //重新獲取線程狀態(tài)
                    int rs = runStateOf(ctl.get());
					// 狀態(tài)小于shutdown 就是running狀態(tài) 或者 為shutdown并且firstTask為空是從隊(duì)列中處理      任務(wù)那就可以放到集合中
                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                       // 線程還沒(méi)start就是alive就直接異常
                        if (t.isAlive()) 
                            throw new IllegalThreadStateException();
                        workers.add(w);
                        int s = workers.size();
                        if (s > largestPoolSize)
                        // 記錄最大線程數(shù)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
            //失敗回退從wokers移除w線程數(shù)減1嘗試結(jié)束線程池
                addWorkerFailed(w);
        }
        return workerStarted;
    }
    private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
    {
        /**
         * This class will never be serialized, but we provide a
         * serialVersionUID to suppress a javac warning.
         */
        private static final long serialVersionUID = 6138294804551838833L;
        /** Thread this worker is running in.  Null if factory fails. */
        //正在運(yùn)行woker線程
        final Thread thread;
        /** Initial task to run.  Possibly null. */
        //傳入的任務(wù)
        Runnable firstTask;
        /** Per-thread task counter */
        //完成的任務(wù)數(shù)監(jiān)控用
        volatile long completedTasks;
        /**
         * Creates with given first task and thread from ThreadFactory.
         * @param firstTask the first task (null if none)
         */
        Worker(Runnable firstTask) {
            //禁止線程中斷
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }
        /** Delegates main run loop to outer runWorker  */
        public void run() {
            runWorker(this);
        }

再來(lái)看runworker方法

final void runWorker(Worker w) {
		//獲取當(dāng)前線程
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts 把state從‐1改為0意思是可以允許中斷
        boolean completedAbruptly = true;
        try {
        	//task不為空或者阻塞隊(duì)列中拿到了任務(wù)
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                //如果當(dāng)前線程池狀態(tài)等于stop就中斷
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);
                    }
                } finally {
                  //這設(shè)置為空等下次循環(huán)就會(huì)從隊(duì)列里面獲取 
                    task = null;
                    //完成任務(wù)數(shù)+1
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }

獲取任務(wù)的方法

    private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);//獲取線程池運(yùn)行狀態(tài)
            // Check if queue empty only if necessary.
            //shutdown或者為空那就工作線程‐1同時(shí)返回為null
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }
			//重新獲取工作線程數(shù)
            int wc = workerCountOf(c);
            // Are workers subject to culling?
            // timed是標(biāo)志超時(shí)銷毀 核心線程池也是可以銷毀的
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }
            try {
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

runWorker中的processWorkerExit

    private void processWorkerExit(Worker w, boolean completedAbruptly) {
        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
            decrementWorkerCount();
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            completedTaskCount += w.completedTasks;
            workers.remove(w);
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
        int c = ctl.get();
        if (runStateLessThan(c, STOP)) {
            if (!completedAbruptly) {
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
                if (min == 0 && ! workQueue.isEmpty())
                    min = 1;
                if (workerCountOf(c) >= min)
                    return; // replacement not needed
            }
            addWorker(null, false);
        }
    }

ThreadPoolExecutor內(nèi)部有實(shí)現(xiàn)4個(gè)拒絕策略:(1)、

  • CallerRunsPolicy,由調(diào)用execute方法提交任務(wù)的線程來(lái)執(zhí)行這個(gè)任務(wù);
  • AbortPolicy,拋出異常RejectedExecutionException拒絕提交任務(wù);
  • DiscardPolicy,直接拋棄任務(wù),不做任何處理;
  • DiscardOldestPolicy,去除任務(wù)隊(duì)列中的第一個(gè)任務(wù)(最舊的),重新提交

ScheduledThreadPoolExecutor

  • schedule:延遲多長(zhǎng)時(shí)間之后只執(zhí)行一次;
  • scheduledAtFixedRate固定:延遲指定時(shí)間后執(zhí)行一次,之后按照固定的時(shí)長(zhǎng)周期執(zhí)行;
  • scheduledWithFixedDelay非固定:延遲指定時(shí)間后執(zhí)行一次,之后按照:上一次任務(wù)執(zhí)行時(shí)長(zhǎng)+周期的時(shí)長(zhǎng)的時(shí)間去周期執(zhí)行;
   private void delayedExecute(RunnableScheduledFuture<?> task) {
   		//如果線程池不是RUNNING狀態(tài),則使用拒絕策略把提交任務(wù)拒絕掉
        if (isShutdown())
            reject(task);
        else {
        //與ThreadPoolExecutor不同,這里直接把任務(wù)加入延遲隊(duì)列
            super.getQueue().add(task);
            //如果當(dāng)前狀態(tài)無(wú)法執(zhí)行任務(wù),則取消
            if (isShutdown() &&
                !canRunInCurrentRunState(task.isPeriodic()) &&
                remove(task))
                task.cancel(false);
            else
            //和ThreadPoolExecutor不一樣,corePoolSize沒(méi)有達(dá)到會(huì)增加Worker;
            //增加Worker,確保提交的任務(wù)能夠被執(zhí)行
                ensurePrestart();
        }
    }

add方法里其實(shí)是調(diào)用了offer方法

public boolean add(Runnable e) {
            return offer(e);
        }
public boolean offer(Runnable x) {
            if (x == null)
                throw new NullPointerException();
            RunnableScheduledFuture<?> e = (RunnableScheduledFuture<?>)x;
            final ReentrantLock lock = this.lock;
            lock.lock();
            try {
                int i = size;
                if (i >= queue.length)
                //容量擴(kuò)增50%
                    grow();
                size = i + 1;
                if (i == 0) {
                    queue[0] = e;
                    setIndex(e, 0);
                } else {
                //插入堆尾
                    siftUp(i, e);
                }
                if (queue[0] == e) {
                //如果新加入的元素成為了堆頂,則原先的leader就無(wú)效了
                    leader = null;
               //由于原先leader已經(jīng)無(wú)效被設(shè)置為null了,這里隨便喚醒一個(gè)線程(未必是原先的leader)來(lái)取走堆頂任務(wù)
                    available.signal();
                }
            } finally {
                lock.unlock();
            }
            return true;
        }

siftup方法:主要是對(duì)隊(duì)列進(jìn)行排序

 private void siftUp(int k, RunnableScheduledFuture<?> key) {
            while (k > 0) {
            //獲取父節(jié)點(diǎn)
                int parent = (k - 1) >>> 1;
                RunnableScheduledFuture<?> e = queue[parent];
                //如果key節(jié)點(diǎn)的執(zhí)行時(shí)間大于父節(jié)點(diǎn)的執(zhí)行時(shí)間,不需要再排序了
                if (key.compareTo(e) >= 0)
                    break;
                    //如果key.compareTo(e)<0,說(shuō)明key節(jié)點(diǎn)的執(zhí)行時(shí)間小于父節(jié)點(diǎn)的執(zhí)行時(shí)間,需要把父節(jié)點(diǎn)移到后面
                queue[k] = e;
                setIndex(e, k);
                //設(shè)置索引為k
                k = parent;
            }
            //key設(shè)置為排序后的位置中
            queue[k] = key;
            setIndex(key, k);
        }

run方法:

public void run() {
			//是否周期性,就是判斷period是否為0
            boolean periodic = isPeriodic();
            //檢查任務(wù)是否可以被執(zhí)行
            if (!canRunInCurrentRunState(periodic))
                cancel(false);
            //如果非周期性任務(wù)直接調(diào)用run運(yùn)行即可
            else if (!periodic)
                ScheduledFutureTask.super.run();
            //如果成功runAndRest,則設(shè)置下次運(yùn)行時(shí)間并調(diào)用reExecutePeriodic
            else if (ScheduledFutureTask.super.runAndReset()) {
                setNextRunTime();
            //需要重新將任務(wù)(outerTask)放到工作隊(duì)列中。此方法源碼會(huì)在后文介紹ScheduledThreadPoolExecutor本身API時(shí)提及
                reExecutePeriodic(outerTask);
            }
        }
		private void setNextRunTime() {
            long p = period;
            //fixed‐rate模式,時(shí)間設(shè)置為上一次時(shí)間+p,這里的時(shí)間只是可以被執(zhí)行的最小時(shí)間,不代表到點(diǎn)就要執(zhí)行
            if (p > 0)
                time += p;
            else
            //fixed‐delay模式,計(jì)算下一次任務(wù)可以被執(zhí)行的時(shí)間, 差不多就是當(dāng)前時(shí)間+delay值
                time = triggerTime(-p);
        }
        long triggerTime(long delay) {
        //如果delay<Long.Max_VALUE/2,則下次執(zhí)行時(shí)間為當(dāng)前時(shí)間+delay,否則為了避免隊(duì)列中出現(xiàn)由于溢出導(dǎo)致的排序紊亂,需要調(diào)用overflowFree來(lái)修正一下delay
        return now() +
            ((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));
    }
    /**
    * 主要就是有這么一種情況:
    * 工作隊(duì)列中維護(hù)任務(wù)順序是基于compareTo的,在compareTo中比較兩個(gè)任務(wù)的順序會(huì)用time相減,負(fù)數(shù)則說(shuō)明優(yōu)先級(jí)高,那么就有可能出現(xiàn)一個(gè)delay為正數(shù),減去另一個(gè)為負(fù)數(shù)的delay,結(jié)果上溢為負(fù)數(shù),則會(huì)導(dǎo)致compareTo產(chǎn)生錯(cuò)誤的結(jié)果.
    * 為了特殊處理這種情況,首先判斷一下隊(duì)首的delay是不是負(fù)數(shù),如果是正數(shù)不用管了,怎么減都不會(huì)溢出。
    * 否則可以拿當(dāng)前delay減去隊(duì)首的delay來(lái)比較看,如果不出現(xiàn)上溢,則整個(gè)隊(duì)列都o(jì)k,排序不會(huì)亂。
    * 不然就把當(dāng)前delay值給調(diào)整為L(zhǎng)ong.MAX_VALUE+隊(duì)首delay
    /
   private long overflowFree(long delay) {
        Delayed head = (Delayed) super.getQueue().peek();
        if (head != null) {
            long headDelay = head.getDelay(NANOSECONDS);
            if (headDelay < 0 && (delay - headDelay < 0))
                delay = Long.MAX_VALUE + headDelay;
        }
        return delay;
    }

到此這篇關(guān)于Java并發(fā)線程池實(shí)例分析講解的文章就介紹到這了,更多相關(guān)Java并發(fā)線程池內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • mybatis collection關(guān)聯(lián)查詢多個(gè)參數(shù)方式

    mybatis collection關(guān)聯(lián)查詢多個(gè)參數(shù)方式

    在使用MyBatis進(jìn)行關(guān)聯(lián)查詢時(shí),往往需要根據(jù)多個(gè)參數(shù)進(jìn)行查詢,例如,使用evtId和businessType作為查詢條件,同時(shí)在resultMap中配置id和businessType1作為結(jié)果映射,這種情況下,可以通過(guò)<sql>標(biāo)簽定義參數(shù)模板,或者使用@Param注解指定參數(shù)名稱
    2024-10-10
  • Java實(shí)現(xiàn)調(diào)用jython執(zhí)行python文件的方法

    Java實(shí)現(xiàn)調(diào)用jython執(zhí)行python文件的方法

    這篇文章主要介紹了Java實(shí)現(xiàn)調(diào)用jython執(zhí)行python文件的方法,結(jié)合實(shí)例形式分析了Java調(diào)用jython執(zhí)行python文件的常見(jiàn)操作技巧及相關(guān)問(wèn)題解決方法,需要的朋友可以參考下
    2018-03-03
  • Java實(shí)現(xiàn)屏幕截圖及剪裁

    Java實(shí)現(xiàn)屏幕截圖及剪裁

    這是一篇入門級(jí)文章,高手請(qǐng)略過(guò)。在這篇文章中我們將學(xué)習(xí)如何用 Java 對(duì)圖像進(jìn)行剪裁并將剪裁出來(lái)的部分單獨(dú)保存到文件中。
    2014-09-09
  • MyBatis中的連接池及事物控制配置過(guò)程

    MyBatis中的連接池及事物控制配置過(guò)程

    連接池就是用于存儲(chǔ)數(shù)據(jù)庫(kù)連接的一個(gè)容器,容器其實(shí)就是一個(gè)集合對(duì)象,本文給大家介紹MyBatis中的連接池以及事物控制的相關(guān)知識(shí),感興趣的朋友一起看看吧
    2021-05-05
  • 使用mybatis的interceptor修改執(zhí)行sql以及傳入?yún)?shù)方式

    使用mybatis的interceptor修改執(zhí)行sql以及傳入?yún)?shù)方式

    這篇文章主要介紹了使用mybatis的interceptor修改執(zhí)行sql以及傳入?yún)?shù)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • Java?IO流之StringWriter和StringReader用法分析

    Java?IO流之StringWriter和StringReader用法分析

    這篇文章主要介紹了Java?IO流之StringWriter和StringReader用法分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • Spring中@PropertySource的使用方法和運(yùn)行原理詳解

    Spring中@PropertySource的使用方法和運(yùn)行原理詳解

    這篇文章主要介紹了Spring中@PropertySource的使用方法和運(yùn)行原理詳解,PropertySource注解可以方便和靈活的向Spring的環(huán)境容器(org.springframework.core.env.Environment?Environment)中注入一些屬性,這些屬性可以在Bean中使用,需要的朋友可以參考下
    2023-11-11
  • Java8實(shí)現(xiàn)任意參數(shù)的鏈棧

    Java8實(shí)現(xiàn)任意參數(shù)的鏈棧

    這篇文章主要為大家詳細(xì)介紹了Java8實(shí)現(xiàn)任意參數(shù)的鏈棧,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-10-10
  • 詳解java封裝返回結(jié)果與RestControllerAdvice注解

    詳解java封裝返回結(jié)果與RestControllerAdvice注解

    這篇文章主要為大家介紹了java封裝返回結(jié)果與RestControllerAdvice注解實(shí)例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09
  • Java?Collections.sort()實(shí)現(xiàn)List排序的默認(rèn)方法和自定義方法

    Java?Collections.sort()實(shí)現(xiàn)List排序的默認(rèn)方法和自定義方法

    這篇文章主要介紹了Java?Collections.sort()實(shí)現(xiàn)List排序的默認(rèn)方法和自定義方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2017-06-06

最新評(píng)論

桐柏县| 自治县| 台前县| 科尔| 周宁县| 随州市| 潮安县| 同心县| 包头市| 皮山县| 赣榆县| 平邑县| 尉氏县| 霍邱县| 靖安县| 密山市| 务川| 平凉市| 泸水县| 泸定县| 郯城县| 宽甸| 尼玛县| 浦北县| 合肥市| 喀喇沁旗| 会宁县| 大宁县| 靖州| 贵溪市| 聂荣县| 太康县| 城固县| 陇川县| 焉耆| 平顶山市| 师宗县| 荔波县| 涟源市| 同德县| 宝兴县|