亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java多線程并發(fā)FutureTask使用詳解

 更新時(shí)間:2022年06月28日 15:03:17   作者:自動(dòng)化BUG制造器  
Java?的多線程機(jī)制本質(zhì)上能夠完成兩件事情,異步計(jì)算和并發(fā),F(xiàn)utureTask?是基于?Runnable?實(shí)現(xiàn)的一個(gè)可取消的異步調(diào)用?API,本文給大家介紹Java?多線程并發(fā)FutureTask及基本使用,需要的朋友可以參考下

本文基于最新的 OpenJDK 代碼,預(yù)計(jì)發(fā)行版本為 19 。

Java 的多線程機(jī)制本質(zhì)上能夠完成兩件事情,異步計(jì)算和并發(fā)。并發(fā)問(wèn)題通過(guò)解決線程安全的一系列 API 來(lái)解決;而異步計(jì)算,常見的使用是 Runnable 和 Callable 配合線程使用。

FutureTask 是基于 Runnable 實(shí)現(xiàn)的一個(gè)可取消的異步調(diào)用 API 。

基本使用

  • Future 代表了異步計(jì)算的結(jié)果,通過(guò) ExecutorService 的 Future<?> submit(Runnable task) 方法,作為返回值使用:
interface ArchiveSearcher { String search(String target); }
class App {
		ExecutorService executor = ...;
    ArchiveSearcher searcher = ...;
    void showSearch(String target) throws InterruptedException {
       Callable<String> task = () -> searcher.search(target);
       Future<String> future = executor.submit(task); // 獲取執(zhí)行結(jié)果
       displayOtherThings(); // do other things while searching
       try {
         displayText(future.get()); // use future
       } catch (ExecutionException ex) { cleanup(); return; }
    }
}
  • FutureTask類是實(shí)現(xiàn)了Runnable的Future的實(shí)現(xiàn),因此可以由Executor執(zhí)行。例如,上述帶有submit的構(gòu)造可以替換為:
class App {
		ExecutorService executor = ...;
    ArchiveSearcher searcher = ...;
    void showSearch(String target) throws InterruptedException {
       Callable<String> task = () -> searcher.search(target);
       // 關(guān)鍵兩行替換
       FutureTask<String> future = new FutureTask<>(task);
			 executor.execute(future);
       displayOtherThings(); // do other things while searching
       try {
         displayText(future.get()); // use future
       } catch (ExecutionException ex) { cleanup(); return; }
    }
}

代碼分析

繼承關(guān)系

Future

Future 表示異步計(jì)算的結(jié)果。定義了用于檢查計(jì)算是否完成、等待計(jì)算完成以及檢索計(jì)算結(jié)果的能力。只有在計(jì)算完成后,才能使用 get 方法檢索結(jié)果,在必要時(shí)會(huì)阻塞線程直到 Future 計(jì)算完成。取消是由 cancel 方法執(zhí)行的。還提供了其他方法來(lái)確定任務(wù)是正常完成還是被取消。一旦計(jì)算完成,就不能取消計(jì)算。如果為了可取消性而使用 Future ,但又不想提供一個(gè)可用的結(jié)果,你可以聲明形式 Future<?> 并返回 null 作為任務(wù)的結(jié)果。

在介紹 Future 中定義的能力之前,先了解一下它的用來(lái)表示 Future 狀態(tài)內(nèi)部類,和狀態(tài)檢索方法:

public interface Future<V> {
    enum State {
       	// The task has not completed.
        RUNNING,
        // The task completed with a result. @see Future#resultNow()
        SUCCESS,
        //The task completed with an exception. @see Future#exceptionNow()
        FAILED,
        // The task was cancelled. @see #cancel(boolean)
        CANCELLED
    }

    default State state() {
        if (!isDone())		// 根據(jù) isDone() 判斷運(yùn)行中
            return State.RUNNING;
        if (isCancelled()) // 根據(jù) isCancelled() 判斷已取消
            return State.CANCELLED;
        boolean interrupted = false;
        try {
            while (true) { // 死循環(huán)輪詢
                try {
                    get();  // may throw InterruptedException when done
                    return State.SUCCESS; 
                } catch (InterruptedException e) {
                    interrupted = true;
                } catch (ExecutionException e) {
                    return State.FAILED;
                }
            }
        } finally {
            if (interrupted) Thread.currentThread().interrupt();
        }
    }
}

Future 的狀態(tài)檢索的默認(rèn)實(shí)現(xiàn)是根據(jù) isDone() 、isCancelled() 和不斷輪詢 get() 方法獲取到的返回值判斷的。

當(dāng) get() 正常返回結(jié)果時(shí), state() 返回 State.SUCCESS ; 當(dāng)拋出 InterruptedException 時(shí),最終會(huì)操作所在的線程執(zhí)行嘗試中斷的方法;拋出其他異常時(shí),則返回 State.FAILED

Future 中定義的其他方法包括:

package java.util.concurrent;
public interface Future<V> {
		// 取消操作
    boolean cancel(boolean mayInterruptIfRunning);
		// 檢查是否取消
    boolean isCancelled();
		// 檢查是否完成
    boolean isDone();
		// 獲取計(jì)算結(jié)果的方法
    V get() throws InterruptedException, ExecutionException;
		// 帶有超時(shí)限制的獲取計(jì)算結(jié)果的方法
    V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
  	// 立刻返回結(jié)果
  	default V resultNow()
  	// 立刻拋出異常
  	default Throwable exceptionNow()
}

其中 resultNow()exceptionNow() 是帶有默認(rèn)實(shí)現(xiàn)的:

		default V resultNow() {
        if (!isDone())
            throw new IllegalStateException("Task has not completed");
        boolean interrupted = false;
        try {
            while (true) {
                try {
                    return get();
                } catch (InterruptedException e) {
                    interrupted = true;
                } catch (ExecutionException e) {
                    throw new IllegalStateException("Task completed with exception");
                } catch (CancellationException e) {
                    throw new IllegalStateException("Task was cancelled");
                }
            }
        } finally {
            if (interrupted) Thread.currentThread().interrupt();
        }
    }
  • Future 仍在運(yùn)行中,直接拋出 IllegalStateException 。
  • 執(zhí)行一個(gè)輪詢,調(diào)用 get() 嘗試返回計(jì)算結(jié)果,如果 get() 拋出異常,則根據(jù)異常拋出不同消息的 IllegalStateException 或執(zhí)行中斷線程的操作。
    default Throwable exceptionNow() {
        if (!isDone())
            throw new IllegalStateException("Task has not completed");
        if (isCancelled())
            throw new IllegalStateException("Task was cancelled");
        boolean interrupted = false;
        try {
            while (true) {
                try {
                    get();
                    throw new IllegalStateException("Task completed with a result");
                } catch (InterruptedException e) {
                    interrupted = true;
                } catch (ExecutionException e) {
                    return e.getCause();
                }
            }
        } finally {
            if (interrupted) Thread.currentThread().interrupt();
        }
    }
  • Future 仍在運(yùn)行中,直接拋出 IllegalStateException 。
  • Future 檢查是否已取消,如果取消了拋出 IllegalStateException 。
  • 執(zhí)行輪詢,調(diào)用 get() 方法,如果能夠正常執(zhí)行結(jié)束,也拋出 IllegalStateException ,消息是 "Task completed with a result" ;get() 若拋出 InterruptedException ,則執(zhí)行線程中斷操作;其他異常正常拋出。

這就是 Future 的全貌了。

RunnableFuture

RunnableFuture 接口同時(shí)實(shí)現(xiàn)了 Runnable 和 Future 接口 :

public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     * 除非已取消,否則將此Future設(shè)置為其計(jì)算的結(jié)果。
     */
    void run();
}

Runnable 接口是我們常用的用來(lái)實(shí)現(xiàn)線程操作的,可以說(shuō)是十分熟悉也十分簡(jiǎn)單了。

這個(gè)接口代表了一個(gè)可以 Runnable 的 Future ,run 方法的成功執(zhí)行代表著 Future 執(zhí)行完成,并可以獲取它的計(jì)算結(jié)果。

這個(gè)接口是 JDK 1.6 后續(xù)才有的。

FutureTask

FutureTask 是 RunnableFuture 的直接實(shí)現(xiàn)類,它代表了一個(gè)可取消的異步計(jì)算任務(wù)。根據(jù)我們對(duì) Future 的分析和 Runnable 的熟悉,就可以理解它的作用了:可取消并可以檢索運(yùn)行狀態(tài)的一個(gè) Runnable ,配合線程使用可以中斷線程執(zhí)行。當(dāng)任務(wù)沒(méi)有執(zhí)行完成時(shí)會(huì)造成阻塞。并且它還可以配合 Executor 使用。

狀態(tài)

FutureTask 內(nèi)部也定義了自己的狀態(tài):

public class FutureTask<V> implements RunnableFuture<V> {
		private volatile int state;
    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; // 已中斷
  
		@Override
    public State state() {
        int s = state;
        while (s == COMPLETING) {
            // 等待過(guò)渡到 NORMAL 或 EXCEPTIONAL
            Thread.yield();
            s = state;
        }
        switch (s) {
            case NORMAL:
                return State.SUCCESS;
            case EXCEPTIONAL:
                return State.FAILED;
            case CANCELLED:
            case INTERRUPTING:
            case INTERRUPTED:
                return State.CANCELLED;
            default:
                return State.RUNNING;
        }
    }
}

FutureTask 的狀態(tài)包括 7 種,最初為 NEW ,只有在 set、setException 和 cancel 方法中,運(yùn)行狀態(tài)才會(huì)轉(zhuǎn)換為最終狀態(tài)。在完成期間,狀態(tài)可能為 COMPLETING (當(dāng)結(jié)果正在設(shè)置時(shí)) 或 INTERRUPTING (僅當(dāng)中斷跑者以滿足cancel(true) )的瞬態(tài)值。

可能存在的狀態(tài)轉(zhuǎn)換是:

NEW -> COMPLETING -> NORMAL // 正常完成
NEW -> COMPLETING -> EXCEPTIONAL // 拋出異常
NEW -> CANCELLED // 取消
NEW -> INTERRUPTING -> INTERRUPTED // 中斷

屬性

下面分析一下它的屬性:

    /** 底層的調(diào)用;運(yùn)行后為null */
    private Callable<V> callable;
    /** get()返回的結(jié)果或拋出的異常 */
    private Object outcome; // non-volatile, protected by state reads/writes
    /** The thread running the callable; CASed during run() */
    private volatile Thread runner;
    /** 等待線程的 Treiber 堆棧 */
    private volatile WaitNode waiters;

內(nèi)部類

先看一看這個(gè) WaitNode ,這是一個(gè) FutureTask 的內(nèi)部類:

    static final class WaitNode {
        volatile Thread thread;
        volatile WaitNode next;
        WaitNode() { thread = Thread.currentThread(); }
    }

一個(gè)鏈表結(jié)構(gòu),用來(lái)對(duì)等待線程進(jìn)行排序。

構(gòu)造方法

最后是方法的分析,首先是構(gòu)造方法:

    // Creates a {@code FutureTask} that will, upon running, execute the given {@code Callable}.
    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }

    /**
     * Creates a {@code FutureTask} that will, upon running, execute the
     * given {@code Runnable}, and arrange that {@code get} will return the
     * given result on successful completion. 
     * Runnable 成功是返回給定的結(jié)果 result
     */
    public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }

FutureTask 接收一個(gè) Callable 或一個(gè) Runnable 作為參數(shù),Runnable 會(huì)封裝一下都保存到屬性 callable ,然后更新 FutureTask 的狀態(tài)為 NEW 。

從 Future 接口中實(shí)現(xiàn)的方法逐個(gè)分析:

檢索 FutureTask 狀態(tài)

    public boolean isCancelled() {
        return state >= CANCELLED; // 大于等于 4, 已取消、中斷中、已中斷
    }

    public boolean isDone() {
        return state != NEW; // 不是 new 就代表執(zhí)行結(jié)束了
    }

取消操作

    // mayInterruptIfRunning 表示最終的取消是通過(guò)中斷還是通過(guò)取消。
		public boolean cancel(boolean mayInterruptIfRunning) {
        if (!(state == NEW && STATE.compareAndSet(this, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))  // 嘗試設(shè)置 CANCELLED 或 INTERRUPTING 
            return false;
        try {    // in case call to interrupt throws exception
            if (mayInterruptIfRunning) {
                try {
                    Thread t = runner;
                    if (t != null)
                        t.interrupt(); // 通過(guò)中斷取消任務(wù)
                } finally { // final state
                    STATE.setRelease(this, INTERRUPTED); // 更新中斷狀態(tài)
                }
            }
        } finally {
            finishCompletion();
        }
        return true;
    }

這里的 finishCompletion() 的作用是通過(guò) LockSupport 喚醒等待的全部線程并從等待列表中移除,然后調(diào)用done(),最后把 callable 置空。相當(dāng)于取消成功后釋放資源的操作。

    private void finishCompletion() {
        // assert state > COMPLETING;
        for (WaitNode q; (q = waiters) != null;) {
            if (WAITERS.weakCompareAndSet(this, q, null)) {
                for (;;) {
                    Thread t = q.thread;
                    if (t != null) {
                        q.thread = null;
                        LockSupport.unpark(t);
                    }
                    WaitNode next = q.next;
                    if (next == null)
                        break;
                    q.next = null; // unlink to help gc
                    q = next;
                }
                break;
            }
        }
        done();
        callable = null;        // to reduce footprint
    }

done() 是個(gè)空實(shí)現(xiàn),供子類去自定義的。

protected void done() { }

計(jì)算結(jié)果

    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L); // 異步結(jié)果
        return report(s);
    }
    public V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException {
        if (unit == null)
            throw new NullPointerException();
        int s = state;
        if (s <= COMPLETING &&
            (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
            throw new TimeoutException();
        return report(s);
    }

這里涉及兩個(gè)方法:awaitDone 方法和 report 方法 。

awaitDone 方法:

    private int awaitDone(boolean timed, long nanos)
        throws InterruptedException {
        // The code below is very delicate, to achieve these goals:
        // - if nanos <= 0L, 及時(shí)返回,不需要 allocation 或 nanoTime
        // - if nanos == Long.MIN_VALUE, don't underflow
        // - if nanos == Long.MAX_VALUE, and nanoTime is non-monotonic
        //   and we suffer a spurious wakeup, we will do no worse than
        //   to park-spin for a while
        long startTime = 0L;    // Special value 0L means not yet parked
        WaitNode q = null;
        boolean queued = false;
        for (;;) {
            int s = state;
            if (s > COMPLETING) {  // COMPLETING = 1
                if (q != null)
                    q.thread = null;
                return s;
            }
            else if (s == COMPLETING) // 瞬時(shí)態(tài),完成中
                // We may have already promised (via isDone) that we are done
                // so never return empty-handed or throw InterruptedException
                Thread.yield();
            else if (Thread.interrupted()) {
                removeWaiter(q); // 線程中斷,移除等待的線程
                throw new InterruptedException();
            }
            else if (q == null) {
                if (timed && nanos <= 0L)
                    return s;
                q = new WaitNode();
            }
            else if (!queued)
                queued = WAITERS.weakCompareAndSet(this, q.next = waiters, q);
            else if (timed) { // 設(shè)置超時(shí)時(shí)間的情況
                final long parkNanos;
                if (startTime == 0L) { // first time
                    startTime = System.nanoTime();
                    if (startTime == 0L)
                        startTime = 1L;
                    parkNanos = nanos;
                } else {
                    long elapsed = System.nanoTime() - startTime;
                    if (elapsed >= nanos) {
                        removeWaiter(q);
                        return state;
                    }
                    parkNanos = nanos - elapsed;
                }
                // nanoTime may be slow; recheck before parking
                if (state < COMPLETING)
                    LockSupport.parkNanos(this, parkNanos);
            }
            else
                LockSupport.park(this);
        }
    }

通過(guò) CAS 和 LockSupport 的掛起/喚醒操作配合,阻塞當(dāng)前線程,異步地等待計(jì)算結(jié)果。

這里有個(gè) removeWaiter 方法,內(nèi)部就是遍歷 waiters ,刪除超時(shí)和中斷的等待線程。

當(dāng)異步邏輯執(zhí)行完成后,調(diào)用 report 方法:

    // 為完成的任務(wù)返回結(jié)果或拋出異常
    private V report(int s) throws ExecutionException {
        Object x = outcome;
        if (s == NORMAL)
            return (V)x;
        if (s >= CANCELLED)
            throw new CancellationException();
        throw new ExecutionException((Throwable)x);
    }

這里用到了一個(gè) outcome ,它是一個(gè) Object 類型,作為返回結(jié)果,通過(guò) set 方法可以對(duì)它進(jìn)行設(shè)置:

    // 除非該 future 已被設(shè)置或取消,否則將該 future 的結(jié)果設(shè)置為給定值。
		// 該方法在成功完成計(jì)算后由 run 方法在內(nèi)部調(diào)用。
		protected void set(V v) {
        if (STATE.compareAndSet(this, NEW, COMPLETING)) {
            outcome = v;
            STATE.setRelease(this, NORMAL); // final state
            finishCompletion();
        }
    }

立刻獲取結(jié)果或異常

這兩個(gè)方法都是通過(guò) outcome 預(yù)設(shè)的返回值,返回預(yù)期的結(jié)果或異常。

    public V resultNow() {
        switch (state()) {    // Future.State
            case SUCCESS:
                @SuppressWarnings("unchecked")
                V result = (V) outcome;
                return result;
            case FAILED:
                throw new IllegalStateException("Task completed with exception");
            case CANCELLED:
                throw new IllegalStateException("Task was cancelled");
            default:
                throw new IllegalStateException("Task has not completed");
        }
    }

    @Override
    public Throwable exceptionNow() {
        switch (state()) {    // Future.State
            case SUCCESS:
                throw new IllegalStateException("Task completed with a result");
            case FAILED:
                Object x = outcome;
                return (Throwable) x;
            case CANCELLED:
                throw new IllegalStateException("Task was cancelled");
            default:
                throw new IllegalStateException("Task has not completed");
        }
    }

run 方法組

最后是實(shí)現(xiàn)了 Runnable 的 run 方法:

    public void run() {
      	// 保證 NEW 狀態(tài)和 RUNNER 成功設(shè)置當(dāng)前線程
        if (state != NEW ||
            !RUNNER.compareAndSet(this, null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable; // 待執(zhí)行的 Callable
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call(); // 執(zhí)行 Callable 
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            // 為了防止并發(fā)調(diào)用 run ,直到 state 確定之前, runner 必須是非空的
            runner = null;
            // 狀態(tài)必須在 runner 置空后重新讀取,以防止泄漏中斷
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

這里涉及兩個(gè)方法,第一個(gè)是 setException(ex) :

    // 導(dǎo)致該future報(bào)告一個(gè){@link ExecutionException},并將給定的可拋出對(duì)象作為其原因,除非該future已經(jīng)被設(shè)置或取消。
    protected void setException(Throwable t) {
        if (STATE.compareAndSet(this, NEW, COMPLETING)) {
            outcome = t;
            STATE.setRelease(this, EXCEPTIONAL); // final state
            finishCompletion();
        }
    }

另一個(gè)是 handlePossibleCancellationInterrupt 方法:

    /**
     * 確保任何來(lái)自可能的 cancel(true) 的中斷只在 run 或 runAndReset 時(shí)交付給任務(wù)。
     */
    private void handlePossibleCancellationInterrupt(int s) {
        // It is possible for our interrupter to stall before getting a
        // chance to interrupt us.  Let's spin-wait patiently.
        if (s == INTERRUPTING)
            while (state == INTERRUPTING)
                Thread.yield(); // wait out pending interrupt

        // assert state == INTERRUPTED;
        // 我們想清除可能從cancel(true)接收到的所有中斷。
        // 然而,允許使用中斷作為任務(wù)與其調(diào)用者通信的獨(dú)立機(jī)制,并沒(méi)有辦法只清除取消中斷。
        // Thread.interrupted();
    }

最后是 runAndReset 方法:

    protected boolean runAndReset() {
        if (state != NEW || !RUNNER.compareAndSet(this, null, Thread.currentThread()))
            return false;
        boolean ran = false; // flag 表示正常執(zhí)行結(jié)束
        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; // 當(dāng)正常執(zhí)行結(jié)束,且 state 一開始就是 NEW 時(shí),表示可以運(yùn)行并重置。
    }

執(zhí)行計(jì)算時(shí)不設(shè)置其結(jié)果,然后將該 future 重置為初始狀態(tài),如果計(jì)算遇到異常或被取消,則不這樣做。這是為本質(zhì)上執(zhí)行多次的任務(wù)設(shè)計(jì)的。

run 和 runAndReset 都用到了一個(gè) RUNNER , 最后就來(lái)揭秘一下這個(gè)東西:

    private static final VarHandle STATE;
    private static final VarHandle RUNNER;
    private static final VarHandle WAITERS;
    static {
        try {
            MethodHandles.Lookup l = MethodHandles.lookup();
            STATE = l.findVarHandle(FutureTask.class, "state", int.class);
            RUNNER = l.findVarHandle(FutureTask.class, "runner", Thread.class);
            WAITERS = l.findVarHandle(FutureTask.class, "waiters", WaitNode.class);
        } catch (ReflectiveOperationException e) {
            throw new ExceptionInInitializerError(e);
        }

        // Reduce the risk of rare disastrous classloading in first call to
        // LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773
        Class<?> ensureLoaded = LockSupport.class;
    }

MethodHandles.lookup()創(chuàng)建一個(gè)MethodHandles.Lookup對(duì)象,該對(duì)象可以創(chuàng)建所有訪問(wèn)權(quán)限的方法,包括public,protectedprivate,default。

VarHandle 主要用于動(dòng)態(tài)操作數(shù)組的元素或?qū)ο蟮某蓡T變量。VarHandle通過(guò) MethodHandles 來(lái)獲取實(shí)例,然后調(diào)用 VarHandle 的方法即可動(dòng)態(tài)操作指定數(shù)組的元素或指定對(duì)象的成員變量。

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

相關(guān)文章

  • Spring?@DateTimeFormat日期格式化時(shí)注解場(chǎng)景分析

    Spring?@DateTimeFormat日期格式化時(shí)注解場(chǎng)景分析

    這篇文章主要介紹了Spring?@DateTimeFormat日期格式化時(shí)注解場(chǎng)景分析,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-05-05
  • JAVA?GUI基礎(chǔ)與MouseListener用法

    JAVA?GUI基礎(chǔ)與MouseListener用法

    這篇文章主要介紹了JAVA?GUI基礎(chǔ)與MouseListener用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • eclipse修改maven倉(cāng)庫(kù)位置的方法實(shí)現(xiàn)

    eclipse修改maven倉(cāng)庫(kù)位置的方法實(shí)現(xiàn)

    本文主要介紹了eclipse修改maven倉(cāng)庫(kù)位置的方法實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08
  • 詳解Java實(shí)現(xiàn)多線程的三種方式

    詳解Java實(shí)現(xiàn)多線程的三種方式

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)多線程的三種方式,感興趣的小伙伴們可以參考一下
    2016-03-03
  • SpringBoot自定義/error路徑失效的解決

    SpringBoot自定義/error路徑失效的解決

    這篇文章主要介紹了SpringBoot自定義/error路徑失效的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • Java設(shè)計(jì)模式七大原則之接口隔離原則詳解

    Java設(shè)計(jì)模式七大原則之接口隔離原則詳解

    接口隔離原則(Interface Segregation Principle),又稱為ISP原則,就是在一個(gè)類中不要定義過(guò)多的方法,接口應(yīng)該盡量簡(jiǎn)單細(xì)化。本文將為大家具體介紹一下Java設(shè)計(jì)模式七大原則之一的接口隔離原則,需要的可以參考一下
    2022-02-02
  • Spring MVC的文件上傳和下載以及攔截器的使用實(shí)例

    Spring MVC的文件上傳和下載以及攔截器的使用實(shí)例

    這篇文章主要介紹了Spring MVC的文件上傳和下載以及攔截器的使用實(shí)例,具有一定的參考價(jià)值,有興趣的可以了解一下
    2017-08-08
  • 使用Lombok @Builder注解導(dǎo)致默認(rèn)值無(wú)效的問(wèn)題

    使用Lombok @Builder注解導(dǎo)致默認(rèn)值無(wú)效的問(wèn)題

    這篇文章主要介紹了使用Lombok @Builder注解導(dǎo)致默認(rèn)值無(wú)效的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • 詳解SpringBoot和Mybatis配置多數(shù)據(jù)源

    詳解SpringBoot和Mybatis配置多數(shù)據(jù)源

    本篇文章主要介紹了詳解SpringBoot和Mybatis配置多數(shù)據(jù)源,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-05-05
  • Java 的可變參數(shù)方法詳述

    Java 的可變參數(shù)方法詳述

    這篇文章主要介紹了Java 的可變參數(shù)方法,可變參數(shù)只能作為函數(shù)的最后一個(gè)參數(shù),在其前面可以有也可以沒(méi)有任何其他參數(shù),由于可變參數(shù)必須是最后一個(gè)參數(shù),所以一個(gè)函數(shù)最多只能有一個(gè)可變參數(shù),下面我們一起進(jìn)入文章了解更多關(guān)于可變參數(shù)的內(nèi)容吧
    2022-02-02

最新評(píng)論