1. 程式人生 > >《JAVA併發程式設計實戰》任務執行

《JAVA併發程式設計實戰》任務執行

文章目錄

線上程中執行任務

當圍繞“任務執行”來設計應用程式結構時,第一步是找出清晰的任務邊界。在理想情況下,各個任務之間是相互獨立的。

在正常的負載下,伺服器應用程式應該同時表現出良好的吞吐量和快速的響應性。應用程式提供商希望程式支援儘可能多的使用者,從而降低每個使用者的服務成本,而使用者則希望獲得儘快的響應。而且,當符合過載時,應用程式的效能應該是逐漸降低的,而不是直接失敗。

大多數伺服器應用程式都提供了一種自然的任務邊界選擇方式:以獨立的客戶請求為邊界。

序列執行任務

在應用程式中單個執行緒中序列的執行各項任務是最簡單的策略。

class SingleThreadWebServer{
    public static void main(String[] args) throws IOException {
        ServerSocket socket = new ServerSocket(80);
        while(true){
            Socket connection = socket.accept();
            handleRequest(connection);
        }
} }

執行性比較糟糕,因為一次只能處理一個請求。主執行緒在接受連線和處理相關請求等操作之間不斷的交替執行。當伺服器正在處理請求時,新到來的連線必須等到請求處理完成,然後伺服器再次呼叫accept。

在伺服器應用程式中,序列處理機制通常無法提供高吞吐率或快速響應性。

顯式的為任務建立執行緒

通過為每個請求建立一個新的執行緒來提供服務,從而實現更高的響應性。

class ThreadPerTaskWebServer {
    public static void main(String[] args) throws IOException {
        ServerSocket socket = new ServerSocket(80);
        while(true) {
            final Socket connection = socket.accept();
            Runnable task = new Runnable(){
                public void run(){
                    handleRequest(connection);
                }
            }
            new Thread(task).start();
        }
    }
}

和上面的區別在於,對於每個連線,主迴圈都將建立一個新執行緒來處理請求,而不是在主迴圈中進行處理。由此得出:

  • 任務處理過程從主執行緒中分離出來,使得主迴圈能夠更快的重新等待下一個到來的連線。這使得程式在完成前面的請求之前可以接受新的請求,從而提高響應性。
  • 任務可以並行處理,從而能同時服務多個請求。如果有多個處理器,或者任務由於某種原因被阻塞,程式的吞吐量將得到提高。
  • 任務處理程式碼必須是執行緒安全的,因為有多個任務時會併發的呼叫這段程式碼。

在正常負載情況下,“為每個任務分配一個執行緒”能提高序列執行的效能,只要請求的到達速率不超過伺服器的請求處理能力,那麼這種方法可以同時帶來更快的響應性和更高的吞吐率。

無限制建立執行緒的不足

在生產環境中,上述方法仍然存在一些缺陷:

  • 執行緒生命週期的開銷非常高。執行緒建立和銷燬都有代價,如果為每個請求建立一個新執行緒將銷燬大量的計算資源。
  • 資源消耗。活躍的執行緒會消耗系統資源,尤其是記憶體。如果可執行的執行緒數量多於可以處理器的數量,那麼有些執行緒將閒置。大量的空閒執行緒會佔用許多記憶體。
  • 穩定性。在可建立執行緒的數量上存在一個限制。這個限制隨著平臺的不同而不同,並且受多個因素制約,包括JVM啟動引數等。如果破壞了這些限制,那麼很可能丟擲OutOfMemoryError.

為“每個任務分配一個執行緒”的問題在於,它沒有限制可建立執行緒的數量。

Executor框架

執行緒池簡化了執行緒的管理工作,並且jdk提供了一種靈活的執行緒池實現作為Exector的框架的一部分。在java類庫中,任務執行的主要抽象不是Thread,而是Executor.

public interface Executor{
    void execute(Runnable command);
}

該框架能支援多種不同型別的任務執行策略。它提供了一種標準的方法將任務的提交過程和執行過程解耦,並用Runnable來表示任務。Executor的實現還提供了對生命週期的支援,以及統計資訊收集、應用程式管理機制和效能監控等。

Executor基於生產者-消費者模式,提供任務的操作相當於生產者,執行任務的執行緒相當於消費者。

示例:基於Executor的Web伺服器

class TaskExecutionWebServer {
    private static final int SIZE = 100;
    private static final Executor exec = Executors.newFixedThreadPool(SIZE);
    
    public static void main(String[] args) throws IOException {
        ServerSocket socket = new ServerSocket(80);
        while(true) {
            final Socket connection = socket.accept();
            Runnable task = new Runnable() {
                handleRequest(connection);
            }
            exec.execute(task);
        }
    }
}

在上述案例中,通過使用Executor將請求處理任務的提交和任務的實際執行解耦開來,並且只要常用另一種不同的Executor實現就可以改變伺服器的行為。

“每次請求建立新執行緒”

public class ThreadPerTaskExecutor implements Executor{
    public void execute(Runnable r){
        new Thread(r).start();
    };
}

類似單執行緒行為即以同步的方式執行每個任務

public class WithinThreadExecutor implements Executor {
    public void execute(Runnable r) {
        r.run();
    };
}

執行策略

通過將任務的提交和執行解耦,從而無須太大的困難就可以為某種型別的任務指定和修改執行策略。在執行策略中定義了任務執行的:

  • 在什麼(What)執行緒中執行任務
  • 任務按照什麼(What)順序執行(FIFO,LIFO,優先順序)?
  • 有多少個(how many)任務能併發執行?
  • 在佇列中有多少個(how many)任務在等待執行?
  • 如果系統由於過載需要拒絕一個任務,那麼應該選擇哪一個(Which)任務?另外,怎樣(How)通知應用程式有任務被拒絕?
  • 在執行一個任務之前或者之後,一個進行哪些(what)動作?

各種執行策略都是一種資源管理工具,最佳策略取決於可用的計算資源以及對服務質量需求。通過限制併發任務的數量,可用確保應用程式不會由於資源耗盡而失敗,或者由於在稀缺資源上發生競爭而嚴重影響效能。

通過將任務的提交和任務的執行策略分離開來,有助於在部署階段選擇和可用硬體資源最匹配的執行策略。

每當看到下面這種形式的程式碼時:new Thread(runnable).start() 並且你希望獲得一種更加靈活的執行策略時,考慮使用Executor來代替Thread.

執行緒池

管理一組同構工作執行緒的資源池。執行緒池和工作佇列密切相關,其中在工作佇列中儲存了所有等待執行的任務。工作者執行緒(Worker Thread)的任務很簡單:從工作佇列中獲取一個任務,執行任務,然後返回執行緒池並等待下一個任務。

通過重用現有執行緒而不是建立新執行緒可以在處理多個請求時分攤線上程建立和銷燬過程中產生的巨大開銷。另外一個好處是,當請求到達時,工作執行緒通常已經存在,因此不會由於等待建立執行緒而延遲任務的執行,從而提高了響應性。

類庫中提供了一些Executor的建立方式:

  • newFixedThreadPool:建立固定長度的執行緒池,每當提交一個任務時建立一個執行緒,直到達到執行緒池的最大數量,這時執行緒池的規模將不再變化。
  • newCachedThreadPool:建立一個可快取的執行緒池,如果執行緒池的當前規模超過了處理需求時,那麼將回收空閒的執行緒,而當需求增加時,則可以新增新的執行緒,執行緒池的規模不受限制。
  • newSingleThreadExecutor:單執行緒的Executor,它建立單個工作者執行緒來執行任務,如果這個執行緒異常結束,會建立另一個執行緒來代替。確保依照任務在佇列中的順序來序列執行。
  • newScheduledThreadPool:建立一個固定長度的執行緒池,而且以延遲或定時的方式來執行任務,類似於Timer

Executor的生命週期

Executor的實現通常會建立執行緒來執行任務。但JVM只有在所有(非守護)執行緒全部終止後才會退出。因此,如果無法正確的關閉Executor,你們JVM將無法結束。

由於Executor以非同步方式來執行任務,因此在關閉應用程式時,可能採用最平緩的關閉形式(完成所有已啟動的任務,並且不再接受任何新的任務),也可能是最粗暴的形式(停電),或者其他。既然Executor是為應用程式提供服務的,因此他們也是可以被關閉的,並將在關閉操作中受影響的任務的狀態反饋給應用程式。

為了解決執行服務的生命週期問題,Executor擴充套件了ExecutorService介面,添加了一些用於生命週期管理的方法(同時還有一些用於任務提交的便利方法)。

public interface ExecutorService extends Executor {
    void shutdown();
    List<Runnable> shutdownNow();
    boolean isShutdown();
    boolean isTerminated();
    boolean awaitTermination(long timeout,TimeUnit unit) throws InterruptedExeception;
    //...
}

ExecutorService的生命週期有3中狀態:執行、關閉、已終止。
ExecutorService在建立時處於執行狀態。shutdown方法將執行平緩的關閉操作:不再接受新的任務,同時等待已經提交的任務執行完成,包括那些提交了還沒有開始執行的任務。shutdownNow方法執行粗暴的關閉操作:嘗試取消所有執行中的任務,並且不再啟動佇列中尚未開始執行的任務。

class LifeCycleWebServer {
    private int ExecutorService exec =  ...;
    
    public void start() throws IOExeception {
        ServerSocket socket = new ServerSocket(80);
        while(!exec.isShutdown()) {
            try {
                final Socket conn = socket.accept();
                exec.execute(new Runnable() {
                    public void run() {
                        handleRequest(conn);
                    }
                });
            } catch(RejectedExecutionExeception e) {
                if(!exec.isShutdown()){
                    log("task submission rejected",e);
                }
            }
        }
    }
    
    public void stop() {
        exec.shutdown();
    }
    
    void handleRequest(Socket conn) {
        Request req = readRequest(conn);
        if(isShutdownRequest(req)) {
            stop();
        }
        else {
            dispatchRequest(req);
        }
    }
    
}

延遲任務和週期任務

Timer類負責管理延遲任務(100秒後執行該任務)和週期任務(每10秒執行一次該任務),然而Timer存在一些缺陷,因此考慮使用ScheduledThreadPoolExecutor來代替它。

Timer在執行所有定時任務時只會建立一個執行緒。如果某個任務的執行時間過長,那麼將破壞其他TimerTask的定時精準性。

Timer的另一個問題是,如果TimerTask丟擲一個異常,那麼Timer將表現出糟糕的行為。Timer執行緒不捕獲異常,因此TimerTask丟擲未檢查的異常時將終止定時執行緒。這種情況下Timer也不會恢復執行緒的執行,而是錯誤的認為整個Timer都被取消了。因此,已經排程但尚未執行的TimerTask將不會再執行,新的任務也不能被排程。(這個問題稱為執行緒洩漏)

如果要構建自己的排程服務,那麼可以使用DelayQueue,它實現了BlockingQueue,並未ScheduledThreadPoolExecutor提供排程功能。DelayQueue管理著一組Delayed物件。每個Delayed物件都有一個相應的延遲時間:在DelayQueue中,只有某個元素逾期後,才能從DelayQueue中執行take操作。從DelayQueue中返回的物件將根據他們的延遲時間進行排序。

public class OutOfTime {
    public static void main(Stirng[] args) throws Exeception {
        Timer timer = new Timer();
        timer.schedule(new ThrowTask(),1);
        SECONDS.sleep(1);
        timer.schedule(new ThrowTask(),1);
        SECONDS.sleep(5);
    }
    
    static class ThrowTask extends TimerTask {
        public void run() {
            throw new RuntimeExeception();
        }
    }
}

程式在1秒就終止了,並丟擲一個異常“Timer already cancelled”

找出可利用的並行性

Executor框架幫助指定執行策略,但如果要使用Executor,必須將任務表述為一個Runnable.

示例:序列的頁面渲染器

最簡單的方法就是對HTML文件進行序列處理。當遇到文字標籤時,將其繪製到影象快取中。當遇到影象引用時,先通過網路獲取它,然後再將其繪製到影象快取中。
這種方式可能會讓使用者感到煩惱,因為他們必須等待很長時間,直到顯示所有文字。

另一種序列方法更好一些,它先繪製文字元素,同時為影象預留出矩形的佔位空間,在處理完第一遍文字後,程式再開始下載影象,並將它們繪製到相應的佔位空間中。

public class SingleThreadRenderer {
    void renderPage(CharSequence source) {
        renderText(source);
        List<ImageData> imageData = new ArrayList<>();
        for(ImageInfo imageInfo : scanForImageInfo(source)){
            imageData.add(imageInfo.downloadImage());
        }
        for(ImageData data : imageData) {
            renderImage(data);
        }
    }
}

攜帶結果的任務Callable和Future

Runnable和Callable描述的都是抽象的計算任務。這些任務通常是有範圍的,即都有一個明確的起始點,並且最終會結束。Executor執行的任務又4個生命週期階段:建立、提交、開始和完成。由於有些任務可能要執行很長時間,因此通常希望能夠取消這些任務,只有他們能響應中斷,才能取消。取消一個已經完成的任務不會有任何影響。

Future表示一個任務的生命週期,並提供了相應的方法判斷是否已經完成或取消,已經獲取任務的結果和取消任務等。

public interface Callable<V> {
    V call() throws Exception;
}

public interface Futute<V> {
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
    V get() throws InterruptedException,ExecutionException,CancellationException;
    V get(long timeout,TimeUnit unit)throws InterruptedException,ExecutionException,CancellationException,TimeoutException;
}

可以通過許多方法建立一個Future來描述任務。ExecutorService中所有的submit方法都將返回一個Future,從而將一個Runnable或者Callable提交給Executor,並得到一個Future用來獲得任務的執行結果或者取消任務。還可以顯式的為某個指定的Runnable或Callable例項化一個FutureTask.

從java6開始,ExecutorService實現可以改寫AbstractExecutorService中的newTaskFor方法,從而根據已提交的Runnable或者Callable來控制Future的例項化過程。

protected <T> RunnableFuture<T> newTaskFor(Callable<T> task) {
    return new FutureTask<T>(task);
}

通過Future實現頁面渲染器

我們將渲染分為兩個任務:

  1. 渲染所有文字
  2. 下載所有影象

Callable和Future有助於表示這些協同任務之間的互動。

public class FutureRenderer {
    private final ExecutorService executor = ...;
    
    void renderPage(CharSequence source) {
        final List<ImageInfo> imageInfos = scanForImageInfo(source);
        Callable<List<ImageData>> task = new Callable<>() {
            public List<ImageData> call(){
                List<ImageData> result = new ArrayList<>();
                for(ImageInfo imageinfo : imageInfos) {
                    result.add(imageInfo.downloadImage());
                }
            };
        }
        
        Future<ListImageData>> future = executor.submit(task);
        renderText(source);
        
        try {
            List<ImageData> imageData = future.get();
            for(ImageData data : imageData){
                renderImage(data);
            }
        } catch(InterriptedException e){
            Thread.currentThread().interrupt();
            future.cancel(true);
        } catch(ExecutionException e) {
            throw launderThrowable(e.getCause());
        }
    }
}

上面這個類使得渲染文字任務和下載影象資料的任務併發的執行。當所有影象下載完成後會顯示到頁面上,這將提升使用者體驗,不僅使使用者更快的看到結果,還有效的利用了並行性,但我們可以做的更好。使用者不必等到所有影象都下載完成,而希望看到每當下載完一副影象時就立即顯示出來。

在異構任務並行化中存在的侷限

FutureRenderer使用了兩個任務,其中一個負責渲染文字,另一個負責下載影象。如果渲染文字的速度遠遠高於下載影象的速度,那麼程式的最終效能和序列執行的效能差別不大,而程式碼卻變複雜了。

只有大量相互獨立且同構的任務可以併發進行處理時,才能體現出程式的工作負載分配到多個任務中帶來的真正效能提升。

CompletionService:Executor和BlockingQueue

如果向Executor提交了一組計算任務,並且希望在計算完成後獲得結果,那麼可以保留和每個任務關聯的Future,然後反覆使用get方法,同時將引數timeout指定為0,從而通過輪詢來判斷任務是否完成。這種方法雖然可行,但卻有些繁瑣。幸運的是,我們還有一種更好的方法:完成服務(CompletionService)

CompletionService將Executor和BlockingQueue的功能融合在一起。你可以將Callable任務提交給它來執行,然後使用類似於佇列操作的take和poll方法來獲得已完成的結果,而這些結果會在完成時被封裝成Future。ExecutorCompletionService實現了CompletionService,並將計算部分委託給一個Executor.

ExecutorCompletionService的實現非常簡單。在建構函式中建立一個BlockingQueue來儲存計算完成的結果。當計算完成時,呼叫Future-Task中的done方法。當提交某個任務時,該任務將首先包裝成一個QueueingFuture,這是FutureTask的一個子類,然後再改寫子類的done方法,並將結果放入BlockingQueue中。take和poll方法委託給了BlockingQueue,這些方法會在得到結果前阻塞。

private class QueueingFuture<V> extends FutureTask<V> {
    QueueingFuture(Callable<V> c) {
        super(c);
    }
    QueueingFuture(Runnable t,V r) {
        super(t,r);
    }
    
    protected void done() {
        completionQueue.add(this);
    }
}

示例:使用CompletionService實現頁面渲染器

為每一幅影象的下載都建立一個獨立任務,並在執行緒池中執行他們。從而將序列的下載過程轉換為並行的過程:這將減少下載所有影象的總時間。此外,通過CompletionService獲取結果以及使每張圖片都在下載完成後立即顯示出來,能使使用者獲得一個更加動態和更高響應的使用者介面。

public class Renderer {
    private final ExecutorService executor;
    
    Renderer(ExecutorService executor) {
        this.executor = executor;
    }
    
    void renderPage(CharSequence source) {
        List<ImageInfo> info = scanForImageInfo(source);
        CompletionService<ImageData> completionService = new ExecutorCompletionService<ImageData>(executor);
        for(final Imageinfo imageInfo : info) {
            completionService.submit(new Callable<ImageData>(){
                public ImageData call() {
                    return imageInfo.downloadImage();
                }
            });
        }
        
        renderText(source);
        
        try {
            for(int i = 0,n = info.size();t < n;t++){
                Future<ImageData> f = completinService.take();
                ImageData imageData = f.get();
                renderImage(imageData);
            }
        } catch(InterruptedException e) {
            Thread.currentThread().interrpt();
        } catch(ExecutionException e) {
            throw launderThrowable(e.getCause());
        }
    }
}

多個ExecutorCompletionService可以共享一個Executor,因此可以建立一個對於特定計算私有,又能共享一個公共Executor的ExecutorCompletionService。因此CompetionService的作用相當於一組計算的控制代碼,這和Future作為單個計算的控制代碼是非常類似的。通過記錄提交個CompletionService的任務數量,並計算出已經獲得的已完成結果的數量,即使使用一個共享的Executor也能知道已經獲得了所有任務結果的時間。

為任務設定時限

如果某個任務無法在指定時間完成,那麼將不再需要它的結果,此時可以放棄這個任務。在有限時間內執行任務的主要困難在於,要確保得到答案的時間不會超過限定的時間,或者在限定時間內無法獲得答案。在支援時間限制的Future.get中支援這種需求:當結果可用時,它立即返回,否則丟擲TimeoutException.

Page renderPageWithAd() throws InterruptedException {
    long endNanos = System.nanoTime() + TIME_BUDGET;
    Future<Ad> f = exec.submit(new FetchAdTask());
    Page page = renderPageBody();
    Ad ad;
    try {
        long timeLeft = endNanos - System.nanoTime();
        ad = f.get(timeLeft,NANOSECONDS);
    } catch(ExecutionException e){
        ad = DEFAULT_AD;
    } catch(TimeoutException e){
        ad = DEFAULT_AD;
        f.cancel(true);
    }
    page.setAd(ad);
    return page;
}

示例:旅行預定入口網站

invokeAll將多個任務提交到一個ExecutorService並獲得結果。InvokeAll方法的引數為一組任務,並返回一組Future。invokeAll按照任務集合中迭代器的順序間將所有的Future新增到返回的集合中,從而使呼叫者能將各個Future和其表示的Callable關聯起來。當所有任務都執行完畢時,或者呼叫執行緒被中斷時,又或者超過指定時限時,invokeAll將返回。當超過指定時限後,然後還未完成的任務都將會取消。當invokeAll返回後,每個任務要麼正常完成,要麼被取消,客戶端程式碼可以呼叫get或者isCancelled判斷是哪種情況。

在預定時間請求旅遊報價:


            
           

相關推薦

java併發程式設計實戰】—–執行緒基本概念

轉自 http://cmsblogs.com/?p=1638 共享和可變 要編寫執行緒安全的程式碼,其核心在於對共享的和可變的狀態進行訪問。 “共享”就意味著變數可以被多個執行緒同時訪問。我們知道系統中的資源是有限的,不同的執行緒對資源都是具有著同等的使用權。有限、公平就意味著競爭

java併發程式設計實戰執行緒安全性筆記

執行緒安全性 可以在多個執行緒中呼叫,並且線上程之間不會出現錯誤的互動。 原子性 原子性:即一個操作或者多個操作 要麼全部執行並且執行的過程不會被任何因素打斷,要麼就都不執行。 i++ 和 ++i就不是原子性。 ++i 讀取值,將值加1,將值寫入i.”讀取,修改,寫入

Java併發程式設計實戰執行緒池的應用

一、任務與執行策略間的隱形耦合 1、執行緒飢餓死鎖 當任務都是同類、獨立的時候,執行緒池才會有最好的工作表現。如果將耗時的短期的任務混合在一起,除非執行緒池很大,否則會有“塞車”的風險;如果提交的任務要依賴其他的任務,除非池是無限的,否則有產生死鎖的風險。如下程式碼所示,對

JAVA併發程式設計實戰任務執行

文章目錄 線上程中執行任務 序列執行任務 顯式的為任務建立執行緒 無限制建立執行緒的不足 Executor框架 示例:基於Executor的Web伺服器 執行策略 執行緒池 Exe

java併發程式設計實戰》之 執行緒安全性

1.執行緒安全性 當多個執行緒訪問某個類時,不管執行時環境採用何種排程方式或者這些執行緒將如何交替執行,並且在主調程式碼中不需要任何額外的同步或協同,這個類都能表現出正確的行為,那麼這個類就是執行緒安全的。 無狀態物件一定是執行緒安全的,何為無狀態,就是類中不包含任何域,也不包含各種其

Java併發程式設計實戰》筆記3——執行緒池的使用

1、執行緒飢餓死鎖 線上程池中,如果任務依賴於其他任務,那麼可能發生死鎖。在單執行緒的Executor中,如果一個任務將另一個任務提交到同一個Executor,並且等待這個被提交任務的結果,那麼通常會引發死鎖。 如下面程式碼所示: public class Thread

[讀書筆記][Java併發程式設計實戰]第二章 執行緒安全性

                                          第二章 執行緒安全性 1-什麼是執行緒安全的類? 當多個執行緒訪問某一個類時,不管執行時環境採用何種排程方式或者這些執行緒將如何交替執行,並且在主調程式碼中不需要任何額外的同步或協同,這個

Java併發程式設計實戰》—— 第二章 執行緒安全性

編寫執行緒安全的程式碼,核心在於要對狀態訪問操作進行管理,特別是對共享的和可變的狀態的訪問。 物件的狀態 同步機制: synchronized(獨佔的加鎖方式) volatile 顯式鎖 原子變數 多個執行緒訪問同一個變數時,有3種方式保障安全

java併發程式設計實戰》:執行緒同步輔助類之訊號量(semaphore)

1.訊號量的概念: 訊號量是一種計數器,用來保護一個或者多個共享資源的訪問,它是併發程式設計的一種基礎工具,大多數程式語言都提供了這個機制。 2、訊號量控制執行緒訪問流程: 如果執行緒要訪問一個共享資源,它必須先獲得訊號量。如果訊號量的內部計數器大於0,訊號量將減1,然後

Java併發程式設計實戰(5)- 執行緒生命週期

在這篇文章中,我們來聊一下執行緒的生命週期。 [toc] # 概述 執行緒是作業系統中的一個概念,在Java中,它是實現併發程式的主要手段。 Java中的執行緒,本質上就是作業系統中的執行緒。 作業系統中的執行緒有“生老病死”,專業說法就是生命週期,雖然不同的開發語言對於作業系統的執行緒做了不同的封裝

JAVA併發程式設計實戰》避免活躍性危險

文章目錄 死鎖 鎖順序死鎖 動態的鎖順序死鎖 在協作物件之間發生的死鎖 開放呼叫 資源死鎖 死鎖的避免和診斷 支援定時的鎖 使用執行緒轉儲資訊來分析死鎖 其他活躍性危

JAVA併發程式設計實戰》取消和關閉

文章目錄 引言 任務取消 中斷 中斷策略 響應中斷 示例:計時執行 通過Future來實現取消 處理不可中斷的阻塞 採用newTaskFor封裝非標準的取消 停止基於執行緒的服務

JAVA併發程式設計實戰》基礎構建模組

文章目錄 同步容器類 同步容器類的問題 迭代器和ConcurrentModificationException 隱藏迭代器 併發容器 ConcurrentHashMap 額外的原子Map操作

JAVA併發程式設計實戰》物件的組合

文章目錄 設計執行緒安全的類 找出構成物件狀態的所有變數 示例 找出約束狀態變數的不變性條件 例項封閉 java監視器模式 示例:車輛追蹤 執行緒安全性的委託

讀書筆記(java併發程式設計實戰——CompletionService)

原文請參考微信公眾號(歡迎關注公眾號:coding_song):https://mp.weixin.qq.com/s/R50Eh4kTDtA031i-yMUZAw    Callable&Future Callbale描述的是抽象的計算任務,有明確的起點,並且最終會結束

java併發程式設計一一多執行緒之間通訊(一)

1.多執行緒之間如何實現通訊 多執行緒之間通訊,其實就是多個執行緒在操作同一個資源,但是操作的動作不同。 1.1什麼是多執行緒之間通訊? 需求:第一個執行緒寫入(input)使用者,另一個執行緒讀取(out)使用者。實現讀一個,寫一個操作。 1.2多執行緒之間通訊需求?

java併發程式設計一一多執行執行緒安全(四)

##1.java重排序 ###1.1資料依賴性 如果兩個操作訪問同一個變數時,且這兩個操作匯中有一個為寫操作,此時這兩個操作之間就 存在資料依賴性。資料依賴分下列三種類型。 名稱 程式碼示例 說明

java併發程式設計一一多執行執行緒安全(三)

1.多執行緒的三大特性 1.1什麼是原子性 即一個操作或多個操作要麼全部執行並且執行的過程不會被任何因素打斷,要麼就都不執行。 一個很經典的例子就是銀行賬戶轉賬問題: 比如從賬戶A向賬戶B轉1000元,那麼必然包括2個操作:從賬戶A減去1000元,往賬戶B加上1000元。這2

java併發程式設計一一多執行執行緒安全(二)

1.多執行緒死鎖 1.1什麼是多執行緒死鎖? 同步中巢狀同步,導致鎖無法釋放 程式碼示例: class Thread009 implements Runnable { private int trainCount = 100; private Object

java併發程式設計一一多執行執行緒安全(一)

1.什麼是執行緒安全? 1.1為什麼有執行緒安全問題? 當多個執行緒同時共享同一個全域性變臉或靜態變數,做寫的操作時,可能會發生資料衝突的問題, 也就是執行緒安全的問題。但是做讀操作是不會發生資料衝突問題。 舉例:現在有100張火車票,有兩個視窗同時搶火車票,用多執行緒模擬搶