Java 并發專題 : Executor詳細介紹 打造基于Executor的Web服務器

jopen 8年前發布 | 8K 次閱讀 Java開發

轉載標明出處:http://blog.csdn.net/lmj623565791/article/details/26938985

繼續并發,貌似并發的文章很少有人看啊~哈~

今天準備詳細介紹java并發包下的Executor,以及Java提供了很多靈活的且極其方便的線程池的創建。

嗯,那就慢慢說,大家肯定都學過Socket,JavaSe的時候寫聊天程序,游戲的服務器,以及Android程序自己需要提供服務器的,都會拿Socket來自己寫個:

最初我們的服務器可能寫成這樣:

1、單線程服務器

package com.zhy.concurrency.executors;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 單線程Web服務器
 * 
 * @author zhy
 * 
 */
public class SingleThreadWebServer
{

    public static void main(String[] args) throws IOException
    {

        ServerSocket server = new ServerSocket(7711);
        while (true)
        {
            Socket client = server.accept();
            handleReq(client);
        }

    }

    /**
     * 處理請求
     * @param client
     */
    private static void handleReq(Socket client)
    {
    }

}

這樣的服務器代碼很簡單,理論上也是正確的,但是在實現的環境中卻很糟糕,因為它每次只能處理一個請求,如果每個請求的耗時過長,后面的請求會長時間等待或者超時錯誤。

于是乎,出現了下面這種寫法:

2、每個請求分配一個線程的服務器

package com.zhy.concurrency.executors;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
 * 為每個請求分配一個線程
 * @author zhy
 *
 */
public class TaskPreThreadServer
{
    public static void main(String[] args) throws IOException
    {
        ServerSocket server = new ServerSocket(8811);
        while (true)
        {
            final Socket client = server.accept();
            new Thread()
            {
                public void run()
                {
                    handleReq(client);
                };
            }.start();
        }
    }

    protected static void handleReq(Socket client)
    {

    }
}

為每一個請求開辟一個線程,首先我得承認我也經常用這樣的寫法~但是我們還是要吐槽下這樣寫法的不足:

a、線程的生命周期的開銷還是相當高的,大量的線程的創建將消耗大量的計算機資源

b、可創建線程的數量存在一個限制值(這個值由平臺覺得,且受很多因素的制約),如果超過這個限制,可能會報OOM錯誤

c、在一定范圍內,增加線程可以提高系統吞吐量,但是超過了這個范圍,就物極必反了,只會降低程序的執行速度。

所以我們要繼續改進我們的服務器代碼,Executor提供了非常方便的方式:

3、基于Executor的服務器

package com.zhy.concurrency.executors;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

/**
 * 基于Executor的服務器
 * @author zhy
 *
 */
public class TaskExecutionServer
{
    private static final int THREAD_COUNT = 100;
    private static final Executor exec = Executors
            .newFixedThreadPool(THREAD_COUNT);

    public static void main(String[] args) throws IOException
    {
        ServerSocket server = new ServerSocket(9911);
        while (true)
        {
            final Socket client = server.accept();
            Runnable task = new Runnable()
            {
                @Override
                public void run()
                {
                    handleReq(client);
                }
            };
            exec.execute(task);
        }

    }

    protected static void handleReq(Socket client)
    {

    }
}

創建一個固定長度的線程池,既解決了單線程的阻塞問題,也解決了無限創建線程帶來的內存消耗過多等問題。

4、Executors的API介紹

Java類庫提供了許多靜態方法來創建一個線程池:

a、newFixedThreadPool 創建一個固定長度的線程池,當到達線程最大數量時,線程池的規模將不再變化。

b、newCachedThreadPool 創建一個可緩存的線程池,如果當前線程池的規模超出了處理需求,將回收空的線程;當需求增加時,會增加線程數量;線程池規模無限制。

c、newSingleThreadPoolExecutor 創建一個單線程的Executor,確保任務對了,串行執行

d、newScheduledThreadPool 創建一個固定長度的線程池,而且以延遲或者定時的方式來執行,類似Timer;后面后單獨使用Blog介紹它與Timer區別

小結一下:在線程池中執行任務比為每個任務分配一個線程優勢更多,通過重用現有的線程而不是創建新線程,可以在處理多個請求時分攤線程創建和銷毀產生的巨大的開銷。當請求到達時,通常工作線程已經存在,提高了響應性;通過配置線程池的大小,可以創建足夠多的線程使CPU達到忙碌狀態,還可以防止線程太多耗盡計算機的資源。


好了,結束~歡迎大家留言~


來自: http://blog.csdn.net//lmj623565791/article/details/26938985

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!