1. 程式人生 > >面試題-關於Java執行緒池一篇文章就夠了

面試題-關於Java執行緒池一篇文章就夠了

在Java面試中,執行緒池相關知識,雖不能說是必問提,但出現的頻次也是非常高的。同時又鑑於公眾號“程式新視界”的讀者後臺留言讓寫一篇關於Java執行緒池的文章,於是就有本篇內容,本篇將基於Java執行緒池的原理、實現以及相關原始碼進行講解等。

什麼是執行緒池

執行緒池是一種多執行緒處理形式,處理過程中將任務提交到執行緒池,任務的執行交由執行緒池來管理。

為了充分利用CPU多核資源,應用都會採用多執行緒並行/併發計算,最大限度的利用多核提升應用程式效能。

試想一下,如果每個請求都執行一遍建立執行緒、執行任務、銷燬執行緒,那麼對伺服器資源將是一種浪費。在高併發的情況下,甚至會耗盡伺服器資源。

執行緒池的主要作用有兩個:不同請求之間重複利用執行緒,無需頻繁的建立和銷燬執行緒,降低系統開銷和控制執行緒數量上限,避免建立過多的執行緒耗盡程序記憶體空間,同時減少執行緒上下文切換次數。

常見面試題

  • 說說Java執行緒池的好處及實現的原理?
  • Java提供執行緒池各個引數的作用,如何進行的?
  • 根據執行緒池內部機制,當提交新任務時,有哪些異常要考慮?
  • 執行緒池都有哪幾種工作佇列?
  • 使用無界佇列的執行緒池會導致記憶體飆升嗎?
  • 說說幾種常見的執行緒池及使用場景?

執行緒池的建立與使用

在JDK5版本中增加了內建執行緒池實現ThreadPoolExecutor,同時提供了Executors來建立不同型別的執行緒池。Executors中提供了以下常見的執行緒池建立方法:

  • newSingleThreadExecutor:一個單執行緒的執行緒池。如果因異常結束,會再建立一個新的,保證按照提交順序執行。
  • newFixedThreadPool:建立固定大小的執行緒池。根據提交的任務逐個增加執行緒,直到最大值保持不變。如果因異常結束,會新建立一個執行緒補充。
  • newCachedThreadPool:建立一個可快取的執行緒池。會根據任務自動新增或回收執行緒。
  • newScheduledThreadPool:支援定時以及週期性執行任務的需求。
  • newWorkStealingPool:JDK8新增,根據所需的並行層次來動態建立和關閉執行緒,通過使用多個佇列減少競爭,底層使用ForkJoinPool來實現。優勢在於可以充分利用多CPU,把一個任務拆分成多個“小任務”,放到多個處理器核心上並行執行;當多個“小任務”執行完成之後,再將這些執行結果合併起來即可。

雖然在JDK中提供Executors類來支援以上型別的執行緒池建立,但通常情況下不建議開發人員直接使用(見《阿里巴巴java開發規範》)。

執行緒池不允許使用Executors去建立,而是通過ThreadPoolExecutor的方式,這樣的處理方式讓寫的同學更加明確執行緒池的執行規則,規避資源耗盡的風險。

Executors部分方法的弊端:

  • newFixedThreadPool和newSingleThreadExecutor主要問題是堆積的請求處理佇列可能會耗費非常大的記憶體,甚至OOM。
  • newCachedThreadPool和newScheduledThreadPool:主要問題是執行緒數最大數是Integer.MAX_VALUE,可能會建立數量非常多的執行緒,甚至OOM。

同時,阿里巴巴java開發規範中推薦了3種執行緒池建立方式。

方式一,引入commons-lang3包。

//org.apache.commons.lang3.concurrent.BasicThreadFactory
ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1,
    new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").daemon(true).build());

方式二,引入com.google.guava包。

ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
    .setNameFormat("demo-pool-%d").build();

//Common Thread Pool
ExecutorService pool = new ThreadPoolExecutor(5, 200,
    0L, TimeUnit.MILLISECONDS,
    new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());

pool.execute(()-> System.out.println(Thread.currentThread().getName()));
pool.shutdown();//gracefully shutdown

方式三,spring配置執行緒池方式:自定義執行緒工廠bean需要實現ThreadFactory,可參考該介面的其它預設實現類,使用方式直接注入bean,呼叫execute(Runnable task)方法即可。

<bean id="userThreadPool"
    class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
    <property name="corePoolSize" value="10" />
    <property name="maxPoolSize" value="100" />
    <property name="queueCapacity" value="2000" />

<property name="threadFactory" value= threadFactory />
    <property name="rejectedExecutionHandler">
        <ref local="rejectedExecutionHandler" />
    </property>
</bean>
// in code
userThreadPool.execute(thread);

ThreadPoolExecutor的構造方法

除了以上推薦的建立執行緒池的方法,還可以通過ThreadPoolExecutor的構造方法,直接建立執行緒池。本質上來講,以上方法最終也是建立了ThreadPoolExecutor物件,然後堆積進行包裝處理。

ThreadPoolExecutor提供了多個構造方法,我們最終都呼叫的構造方法來進行說明。

 public ThreadPoolExecutor(int corePoolSize,
      int maximumPoolSize,
      long keepAliveTime,
      TimeUnit unit,
      BlockingQueue<Runnable> workQueue,
      ThreadFactory threadFactory,
      RejectedExecutionHandler handler) {
   // 省略程式碼
}

核心引數作用解析如下:

  • corePoolSize:執行緒池核心執行緒數最大值。
  • maximumPoolSize:執行緒池最大執行緒數大小。
  • keepAliveTime:執行緒池中非核心執行緒空閒的存活時間大小。
  • unit:執行緒空閒存活時間單位。
  • workQueue:存放任務的阻塞佇列。
  • threadFactory:建立新執行緒的工廠,所有執行緒都是通過該工廠建立的,有預設實現。
  • handler:執行緒池的拒絕策略。

程池的拒絕策略

構造方法的中最後的引數RejectedExecutionHandler用於指定執行緒池的拒絕策略。當請求任務不斷的過來,而系統此時又處理不過來的時候,我們就需要採取對應的策略是拒絕服務。

預設有四種類型:

  • AbortPolicy策略:該策略會直接丟擲異常,阻止系統正常工作。
  • CallerRunsPolicy策略:只要執行緒池未關閉,該策略直接在呼叫者執行緒中,運行當前的被丟棄的任務。
  • DiscardOleddestPolicy策略:該策略將丟棄最老的一個請求,也就是即將被執行的任務,並嘗試再次提交當前任務。
  • DiscardPolicy策略:該策略默默的丟棄無法處理的任務,不予任何處理。

當然,除了預設的4種策略之外,還可以根據業務需求自定義拒絕策略。通過實現RejectedExecutionHandler介面,在建立ThreadPoolExecutor物件時作為引數傳入即可。

在spring-integration-core中便自定義了CallerBlocksPolicy,相關程式碼如下:

public class CallerBlocksPolicy implements RejectedExecutionHandler {
    private static final Log logger = LogFactory.getLog(CallerBlocksPolicy.class);
    private final long maxWait;

    public CallerBlocksPolicy(long maxWait) {
        this.maxWait = maxWait;
    }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        if (!executor.isShutdown()) {
            try {
                BlockingQueue<Runnable> queue = executor.getQueue();
                if (logger.isDebugEnabled()) {
                    logger.debug("Attempting to queue task execution for " + this.maxWait + " milliseconds");
                }

                if (!queue.offer(r, this.maxWait, TimeUnit.MILLISECONDS)) {
                    throw new RejectedExecutionException("Max wait time expired to queue task");
                } else {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Task execution queued");
                    }

                }
            } catch (InterruptedException var4) {
                Thread.currentThread().interrupt();
                throw new RejectedExecutionException("Interrupted", var4);
            }
        } else {
            throw new RejectedExecutionException("Executor has been shut down");
        }
    }
}

執行緒池的執行

建立完成ThreadPoolExecutor之後,當向執行緒池提交任務時,通常使用execute方法。execute方法的執行流程圖如下:

  • 如果執行緒池中存活的核心執行緒數小於執行緒數corePoolSize時,執行緒池會建立一個核心執行緒去處理提交的任務。
  • 如果執行緒池核心執行緒數已滿,即執行緒數已經等於corePoolSize,一個新提交的任務,會被放進任務佇列workQueue排隊等待執行。
  • 當執行緒池裡面存活的執行緒數已經等於corePoolSize了,並且任務佇列workQueue也滿,判斷執行緒數是否達到maximumPoolSize,即最大執行緒數是否已滿,如果沒到達,建立一個非核心執行緒執行提交的任務。
  • 如果當前的執行緒數達到了maximumPoolSize,還有新的任務過來的話,直接採用拒絕策略處理。

原始碼分析

下面看一下JDK8中ThreadPoolExecutor中execute方法的原始碼實現:

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    // 執行緒池本身的狀態跟worker數量使用同一個變數ctl來維護
    int c = ctl.get();
    // 通過位運算得出當然執行緒池中的worker數量與構造引數corePoolSize進行比較
    if (workerCountOf(c) < corePoolSize) {
        // 如果小於corePoolSize,則直接新增一個worker,並把當然使用者提交的任務command作為引數,如果成功則返回。
        if (addWorker(command, true))
            return;
        // 如果失敗,則獲取最新的執行緒池資料
        c = ctl.get();
    }
    // 如果執行緒池仍在執行,則把任務放到阻塞佇列中等待執行。
    if (isRunning(c) && workQueue.offer(command)) {
        // 這裡的recheck思路是為了處理併發問題
        int recheck = ctl.get();
        // 當任務成功放入佇列時,如果recheck發現執行緒池已經不再運行了則從佇列中把任務刪除
        if (! isRunning(recheck) && remove(command))
            //刪除成功以後,會呼叫構造引數傳入的拒絕策略。
            reject(command);
         // 如果worker的數量為0(此時佇列中可能有任務沒有執行),則新建一個worker(由於此時新建woker的目的是執行佇列中堆積的任務,
         // 因此入參沒有執行任務,詳細邏輯後面會詳細分析addWorker方法)。
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    // 如果前面的新增woker,放入佇列都失敗,則會繼續新增worker,此時執行緒池的狀態是woker數量達到corePoolSize,阻塞佇列任務已滿
    // 只能基於maximumPoolSize引數新建woker
    else if (!addWorker(command, false))
        // 如果基於maximumPoolSize新建woker失敗,此時是執行緒池中執行緒數已達到上限,佇列已滿,則呼叫構造引數中傳入的拒絕策略
        reject(command);
}

下面再看在上述程式碼中呼叫的addWorker方法的原始碼實現及解析:

private boolean addWorker(Runnable firstTask, boolean core) {
    // 這裡有一段基於CAS+死迴圈實現的關於執行緒池狀態,執行緒數量的校驗與更新邏輯就先忽略了,重點看主流程。
    //...

    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
         // 把指定任務作為引數新建一個worker執行緒
        w = new Worker(firstTask);
        // 這裡是重點w.thread是通過執行緒池建構函式引數threadFactory生成的woker物件
        // 也就是說這個變數t就是代表woker執行緒。絕對不是使用者提交的執行緒任務firstTask。
        final Thread t = w.thread;
        if (t != null) {
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                // 加鎖之後仍舊是判斷執行緒池狀態等一些校驗邏輯。
                int rs = runStateOf(ctl.get());
                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
                    if (t.isAlive()) 
                        throw new IllegalThreadStateException();
                    // 把新建的woker執行緒放入集合儲存,這裡使用的是HashSet
                    workers.add(w);
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            if (workerAdded) {
                // 然後啟動woker執行緒
                 // 該變數t代表woker執行緒,會呼叫woker的run方法
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted)
            // 如果woker啟動失敗,則進行一些善後工作,比如說修改當前woker數量等
            addWorkerFailed(w);
    }
    return workerStarted;
}

addWorker方法主要做的工作就是新建一個Woker執行緒,加入到woker集合中。在上述方法中會呼叫到Worker類的run方法,並最終執行了runWorker方法。

// Woker類實現了Runnable介面
public void run() {
    runWorker(this);
}

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    // task就是Woker建構函式入參指定的任務,即使用者提交的任務
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); 
    boolean completedAbruptly = true;
    try {
        //一般情況下,task都不會為空(特殊情況上面註釋中也說明了),因此會直接進入迴圈體中
        //這裡getTask方法是要重點說明的,它的實現跟我們構造引數設定存活時間有關
        //我們都知道構造引數設定的時間代表了執行緒池中的執行緒,即woker執行緒的存活時間,如果到期則回收woker執行緒,這個邏輯的實現就在getTask中。
        //來不及執行的任務,執行緒池會放入一個阻塞佇列,getTask方法就是去阻塞佇列中取任務,使用者設定的存活時間,就是
        //從這個阻塞佇列中取任務等待的最大時間,如果getTask返回null,意思就是woker等待了指定時間仍然沒有
        //取到任務,此時就會跳過迴圈體,進入woker執行緒的銷燬邏輯。
        while (task != null || (task = getTask()) != null) {
            w.lock();
            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 {
                //這裡設為null,也就是迴圈體再執行的時候會呼叫getTask方法
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        //當指定任務執行完成,阻塞佇列中也取不到可執行任務時,會進入這裡,做一些善後工作,比如在corePoolSize跟maximumPoolSize之間的woker會進行回收
        processWorkerExit(w, completedAbruptly);
    }
}

woker執行緒的執行流程就是首先執行初始化時分配給的任務,執行完成以後會嘗試從阻塞佇列中獲取可執行的任務,如果指定時間內仍然沒有任務可以執行,則進入銷燬邏輯。這裡只會回收corePoolSize與maximumPoolSize直接的那部分woker。

execute與submit的區別

執行任務除了可以使用execute方法還可以使用submit方法。它們的主要區別是:execute適用於不需要關注返回值的場景,submit方法適用於需要關注返回值的場景。

異常處理

當執行任務時發生異常,那麼該怎麼處理呢?首先看當Thread執行緒異常如何處理。

在任務中通過try...catch是可以捕獲異常並進行處理的,如下程式碼:

Thread t = new Thread(() -> {
    try {
        System.out.println(1 / 0);
    } catch (Exception e) {
        LOGGER.error(e.getMessage(), e);
    }
});
t.start();

如果很多執行緒任務預設的異常處理機制都是相同的,可以通過Thread類的UncaughtExceptionHandler來設定執行緒預設的異常處理機制。

實現UncaughtExceptionHandler介面,並呼叫Thread#setUncaughtExceptionHandler(UncaughtExceptionHandler)方法。如果想設定為全域性預設異常處理機制,則可呼叫Thread#setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler)方法。

ThreadGroup預設提供了異常處理機制如下:

public void uncaughtException(Thread t, Throwable e) {
    if (parent != null) {
        parent.uncaughtException(t, e);
    } else {
        Thread.UncaughtExceptionHandler ueh =
            Thread.getDefaultUncaughtExceptionHandler();
        if (ueh != null) {
            ueh.uncaughtException(t, e);
        } else if (!(e instanceof ThreadDeath)) {
            System.err.print("Exception in thread \""
                             + t.getName() + "\" ");
            e.printStackTrace(System.err);
        }
    }
}

ThreadPoolExecutor的異常處理機制與Thread是一樣的。同時,ThreadPoolExecutor提供了uncaughtExceptionHandler方法來設定異常處理。如下示例:

public class ThreadPool {

    public static void main(String[] args) {
        ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
                .setNameFormat("demo-pool-%d")
                .setUncaughtExceptionHandler(new LogUncaughtExceptionHandler())
                .build();

        ExecutorService pool = new ThreadPoolExecutor(5, 200,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());

        pool.execute(() -> {
            throw new RuntimeException("測試異常");
        });

        pool.shutdown();
    }

    static class  LogUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {

        @Override
        public void uncaughtException(Thread t, Throwable e) {
            System.out.println("列印LogUncaughtExceptionHandler中獲得的異常資訊:" + e.getMessage());
        }
    }
}

但需要注意的是使用UncaughtExceptionHandler的方法只適用於execute方法執行的任務,而對submit方法是無效。submit執行的任務,可以通過返回的Future物件的get方法接收丟擲的異常,再進行處理。這也算是execute方法與submit方法的差別之一。

執行緒池中常見的佇列

執行緒池有以下工作佇列:

  • ArrayBlockingQueue:有界佇列,是一個用陣列實現的有界阻塞佇列,按FIFO排序量。
  • LinkedBlockingQueue:可設定容量佇列,基於連結串列結構的阻塞佇列,按FIFO排序任務,容量可以選擇進行設定,不設定的話,將是一個無邊界的阻塞佇列,最大長度為Integer.MAX_VALUE,吞吐量通常要高於ArrayBlockingQuene;newFixedThreadPool執行緒池使用了這個佇列。
  • DelayQueue:延遲佇列,是一個任務定時週期的延遲執行的佇列。根據指定的執行時間從小到大排序,否則根據插入到佇列的先後排序。newScheduledThreadPool執行緒池使用了這個佇列。
  • PriorityBlockingQueue:優先順序佇列,是具有優先順序的無界阻塞佇列。
  • SynchronousQueue:同步佇列,一個不儲存元素的阻塞佇列,每個插入操作必須等到另一個執行緒呼叫移除操作,否則插入操作一直處於阻塞狀態,吞吐量通常要高於LinkedBlockingQuene,newCachedThreadPool執行緒池使用了這個佇列。

關閉執行緒池

關閉執行緒池可以呼叫shutdownNow和shutdown兩個方法來實現。

shutdownNow:對正在執行的任務全部發出interrupt(),停止執行,對還未開始執行的任務全部取消,並且返回還沒開始的任務列表。

shutdown:當我們呼叫shutdown後,執行緒池將不再接受新的任務,但也不會去強制終止已經提交或者正在執行中的任務。

參考文章:

https://www.jianshu.com/p/5df6e38e4362

https://juejin.im/post/5d1882b1f265da1ba84aa676

原文連結:《面試題-關於Java執行緒池一篇文章就夠了》


程式新視界:精彩和成長都不容錯過

相關推薦

試題-關於Java執行文章

在Java面試中,執行緒池相關知識,雖不能說是必問提,但出現的頻次也是非常高的。同時又鑑於公眾號“程式新視界”的讀者後臺留言讓寫一篇關於Java執行緒池的文章,於是就有本篇內容,本篇將基於Java執行緒池的原理、實現以及相關原始碼進行講解等。 什麼是執行緒池 執行緒池是一種多執行緒處理形式,處理過程中將任務提

有C/C++/Java基礎的,學習Python文章

我在學校裡學習的是C、C++、C#,實習的時候公司裡用的都是Java,於是又自學了java。用我領導的一句話說,“C++轉Java幾天時間就夠了”。最近在學習深度學習演算法和自然語言處理的時候,很多演算法都是用python寫的,於是又下定決定學習了python。

徹底理解Netty,這文章

Netty到底是什麼 從HTTP說起  有了Netty,你可以實現自己的HTTP伺服器,FTP伺服器,UDP伺服器,RPC伺服器,WebSocket伺服器,Redis的Proxy伺服器,MySQL的Proxy伺服器等等。   我們回顧一下傳統的HTTP伺服器的原理  &n

Android 沉浸式全面詳解(這文章)

方法一:通過設定Theme主題設定狀態列透明 因為 API21 之後(也就是 android 5.0 之後)的狀態列,會預設覆蓋一層半透明遮罩。且為了保持4.4以前系統正常使用,故需要三份 style 檔案,即預設的values(不設定狀態列透明)、values-v19、v

Ubuntu16.04下安裝配置Android studio,sdk,gradle——文章系列

64位ubuntu16.04完美安裝配置jdk、sdk、Android studio 一、下載安裝jdk、sdk、Android studio 注意:我把JDK安裝在了/opt資料夾下面,/opt資料夾和/usr資料夾同樣都是常用做安裝軟體的資料夾,不同的是/usr是

Redis資料庫看這文章

前言 redis作為nosql家族中非常熱門的一員,也是被大型網際網路公司所青睞,無論你是開發、測試或者運維,學習掌握它總會為你的職業生涯增色添彩。 當然,你或多或少已經瞭解redis,但是你是否瞭解其中的某些細節,本片文章將詳細介紹redis基礎,後續也會介紹其高階部分

TensorFlow深度學習,文章

作者: 陳迪豪,就職小米科技,深度學習工程師,TensorFlow程式碼提交者。 TensorFlow深度學習框架 Google不僅是大資料和雲端計算的領導者,在機器學習和深度學習上也有很好的實踐和積累,在2015年年底開源了內部使用的深度學習框架TensorFlow。 與

前端幹活系列----入門webpack這文章

博主學習webpack,並不是因為專案需要,而是因為webpack現在比較流行,所以想學習一下,順便看看對現在的專案是否有用。最後經過思考感覺對當前專案的作用並不大,不過總結了一下webpack適合使用的幾個場景 1.前後端分離的專案,像vue,angular

Android:學習AIDL,這文章(下)

前言 上一篇博文介紹了關於AIDL是什麼,為什麼我們需要AIDL,AIDL的語法以及如何使用AIDL等方面的知識,這一篇博文將順著上一篇的思路往下走,接著介紹關於AIDL的一些更加深入的知識。強烈建議大家在看這篇博文之前先看一下上一篇博文: 注:文中所有程式

【線性代數】矩陣、向量、行列式、特徵值與特徵向量(掌握這些概念文章

在數學領域中,線性代數是一門十分有魅力的學科,首先,它不難學;其次,它能廣泛應用於現實生活中;另外,在機器學習越來越被重視的現在,線性代數也能算得上是一個優秀程式設計師的基本素養吧? 一、線性代數的入門知識 很多人在大學學習線性代數時,國內

Android:學習AIDL,這文章(上)

前言 在決定用這個標題之前甚是忐忑,主要是擔心自己對AIDL的理解不夠深入,到時候大家看了之後說——你這是什麼玩意兒,就這麼點東西就敢說夠了?簡直是坐井觀天不知所謂——那樣就很尷尬了。不過又轉念一想,我輩年輕人自當有一種一往無前的銳氣,標題大氣一點豈不更好?並且大家都是

徹底理解分散式Netty,這文章

一、Netty到底是什麼 1、從HTTP說起 有了Netty,你可以實現自己的HTTP伺服器、FTP伺服器、UDP伺服器、RPC伺服器、WebSocket伺服器、Redis的Proxy伺服器、MySQL的Proxy伺服器等等。 我們回顧一下傳統的HTTP伺服器的原理

express中介軟體,文章

回到目錄 底層:http模組 express目前是最流行的基於Node.js的web開發框架,express框架建立在內建的http模組上, var http = require('http') var app = http.createServer(funct

Activity、View、Window的理解文章

要了解這三者之間的關係,我們帶著問題通過分析原始碼一步一步來揭開它們的神祕面紗! 文章有點長,首先要理解Activity、View、Window,我提出了一些問題,這篇文章可以解答如下問題: 1、為什麼要設計Activity、View、Window? 2、

關於Mysql的事務和鎖 看這文章

鎖共享讀鎖(S鎖)和 排他寫鎖(X鎖)行鎖與表鎖innodb用的是行級鎖,相對於表鎖來說效能開銷會更大。雖然叫做行級鎖,但不表示他只鎖住修改的行記錄,即使找不到行記錄,他也會產生鎖。innodb 是根據掃描範圍來鎖定行記錄,如果有索引,那麼只會鎖定索引的覆蓋範圍,如果找不到索

企業公有云服務的構建“捷徑”,看這文章

最近在上海舉辦的2018世界人工智慧大會,可謂是大佬雲集,金句頻出。在成為業內焦點的同時,也再次

解資料庫安全?看這文章

浪費了“黃金五年”的Java程式設計師,還有救嗎? >>>   

解數據庫安全?看這文章

pri 驗收測試 時間 災難 其中 嚴重 誤操作 了解 雲技術 本文由雲+社區發表 作者:騰訊雲數據庫 互聯網時代,人與人、人與社會交互過程中產生的行為數據、畫像數據、信息數據等正在呈指數級增長,同時數據的價值和重要性不言而喻。數據庫作為數據的載體,產品和技術也越來

關於Python編碼這文章

概述 在使用Python或者其他的程式語言,都會多多少少遇到編碼錯誤,處理起來非常痛苦。在Stack Overflow和其他的程式

學習Canvas這文章

一、canvas簡介​ <canvas> 是 HTML5 新增的,一個可以使用指令碼(通常為JavaScript)在其中繪製圖像的 HTML 元素。它可以用來製作照片集或者製作簡單(也不是那麼簡單)的動畫,甚至可以進行實時視訊處理和渲染。​ 它最初由蘋果內部使用自己MacOS X WebKit推出