1. 程式人生 > >《java併發程式設計實戰》筆記(一) 結構化併發應用程式

《java併發程式設計實戰》筆記(一) 結構化併發應用程式

下載地址

連結:https://pan.baidu.com/s/1i6FlscH 密碼:m21n

1.任務執行

任務是一組邏輯執行單元,執行緒是使得任務非同步執行的機制

不可取的所謂執行緒開啟的方式:
1.所有任務放在單個執行緒中序列執行
2.每一個任務都開啟一個執行緒,無限制,非常浪費資源


有效方法:通過有界佇列防止高負荷的應用程式把記憶體耗盡

使用執行緒池 java.util.concurrent Executor框架

1.1 執行緒池

好處:

  • web伺服器不會在高負載的情況下失敗
  • 伺服器不會建立上千的執行緒來爭奪有限的cpu和記憶體資源
  • Executor 可實現各種調優,管理,監控,日誌

介面

public interface Executor{
    void executor(Runnable command);
}
執行緒池型別 作用
newFixedThreadPool 固定長度的執行緒池,自動維持固定數量的執行緒
newCachedThreadPool 可快取的執行緒池,自動回收空閒的執行緒,如不足,會新增執行緒,執行緒池規模不存在限制
newSingleThreadExecutor 單執行緒的Executor,保持單執行緒序列執行
newScheduledThreadPool 固定數量的執行緒池,延時或者定時執行任務

建立執行緒池

import java.io.IOException;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

public class ExecutorTest {
    private static final int NUM = 100;
    private static final Executor exec = Executors.newFixedThreadPool(NUM);
    private static int index=0;
    private static int count=0;

    public static void main (String[] args) throws IOException {
        while(count++ < 100){
            Runnable task = new Runnable(){
                public void run(){
                    //TODO
                    System.out.println(Thread.currentThread().getName() +"  :"+index++);
                }
            };
            exec.execute(task);
        }
        System.exit(1);
    }
}

result:

pool-1-thread-1  :0
pool-1-thread-3  :2
pool-1-thread-2  :1
pool-1-thread-4  :3
...
pool-1-thread-98  :97
pool-1-thread-99  :98
pool-1-thread-100  :99

不存在超出100的執行緒。

Executor的生命週期

狀態

  • 執行
  • 關閉
  • 已終止

介面

public interface ExecutorService extends Executor{
    void shutdown(); //平緩關閉,不接受新的任務,完成執行中的任務再關閉
    List<Runnable> shutdownNow;   //粗暴關閉,嘗試關閉所有執行中的任務,不接受新的任務
    isTerminated(); //檢視執行緒池是否關閉狀態
    //....
}
class Demo{
    private static final Executor exec = ...
    
    public void start(){
        while(!exec.isShutdown){
        try{
            Runnable task = new Runable(){
                public void run(){
                    //TODO
                }
            };
            exec.execute(task);
        }
        }catch(RejectExecutorException e){
            if(!exec.isShutdown()){
                log.info("task rejected",e);
            }
        }
    }
    
    public void stop(){
        exec.shutdown();
    }
}

延時任務與週期任務

Timer類負責管理這型別任務,但表現非常差,缺點如下:

  • 執行只建立一個執行緒處理所有定時任務,無法保證每個任務的定時準確性。
  • 不捕獲執行中的異常,直接終止定時執行緒,不會恢復執行緒執行。“執行緒洩漏”

正確使用:

執行緒池:ScheduledThreadPoolExector
方法: 構造方法 或者 newScheduledThreadPool()

排程服務:
1.DelyQueue(實現 BlockingQueue)  為 ScheduledThreadPoolExector提供排程服務
2.每個Deplyed物件都有一個相應的延遲時間
3.在DelyQueue中,只有某個物件逾期了,才能執行take操作。

Runnable的缺點

Runnable 不能返回一個值或者丟擲受檢查的異常

攜帶結果的Callable和Future

介面

public interface Callable<V>{
    V call throws Exception;  //V == void ,則表示無返回結果
}

public interface Future<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;
}

使用方式

1.使用Runnable或Callable建立一個例項
2.使用 ExecutorService 的 submit 提交例項 得到 一個 Future
3.通過返回的Future呼叫相應的方法獲得資訊

使用頁面渲染影象(序列下載)

public class FutureRender{
    public static final ExecutorService exec = Executors.newFixedThreadPool(100);
    
    /*
    *渲染頁面
    */
    void renderPage(CharSequence source){
        final List<ImageInfo> imageInfos = scanForImageInfo(source);   //獲得相關影象的資訊
        
        //圖片的下載構建任務 V =  List<ImageDate>
        Callable<List<ImageDate>> task = new Callable(){
            public List<ImageDate> call(){
                List<ImageDate> result = new ArrayList<ImageDate>();  //影象資訊
                for(ImageInfo imageInfo : result){
                    result.add(imageInfo.downImage());
                }
                return result;
            }
        };
        
        rendTexe(source);  //渲染文字
        Future<List<ImageDate>> future = exec.submit(task);   //獲得 Future物件
        
        try{
            List<ImageDate> imageData = future.get(); //future.get( 指定時間,timeUnit) 限時獲得
            for(ImageData iamge : imageData){
                //渲染影象
            }
        }catch(InterruptedException e){
            //重新設定執行緒中斷狀態
            Thread.currentThread.interrupt();
            //取消任務
            future.cancel(true);
        }catch(ExecutiontException e){
            throw launcherThrowable(e.getCause());
        }
    }
}

優點:
文字和圖片並行處理

缺點:
必須等待所有照片下載完

更高效的並行下載

CompletionService 融合了Executor 和 BlockingQueue
可執行Callable任務
最後通過佇列的類似方式獲得Future 結果

ExectorCompletionService 實現了CompletionService
1.提交的任務被包裝成QueueFuture
2.計算結果放入BlockingQueue
3.方法阻塞,等待所有結果算出
public class Render{
    public static final ExecutorService exec = Executors.newFixedThreadPool(100);
    
    /*
    *渲染頁面
    */
    void renderPage(CharSequence source){
        final List<ImageInfo> imageInfos = scanForImageInfo(source);   //獲得相關影象的資訊
        
    
        CompletionService<List<ImageDate>> completionService = new ExectorCompletionService(exec);
        
        for(ImageInfo iamgeInfo : imageInfos){
            completionService.submit(new Callable<ImageDate>(){
                public ImageDate call(){
                    return imageInfo.downImage());
                }
            });
           
        };
        
        rendTexe(source);  //渲染文字
        Future<List<ImageDate>> future = exec.submit(task);   //獲得 Future物件
        
        try{
            for(int i=0; i<imageInfos.size(); i++){
              Future<ImageData> future =  completionService.take();
              ImageData imageData = future.get();
              //渲染圖片
            }
        }catch(InterruptedException e){
            //重新設定執行緒中斷狀態
            Thread.currentThread.interrupt();
        }catch(ExecutiontException e){
            throw launcherThrowable(e.getCause());
        }
    }
}

invokeAll 批量獲得future結果

1.支援限時返回任務結果
2.invokeAll 引數:一組任務,結果:一組Future,有序
3.某個任務未完成則取消,通過get() 或者isCancelled判斷情況

案例:在預定時間內獲得旅遊網站報價

//報價任務
//TravelQuote 相關公司的報價資訊
public class QuoteTask implements Callable<TravelQuote>{
    private final TravelCompany company;  //旅遊公司
    private final TravelInfo travelInfo; //旅遊資訊條件
    
    public TravelQuote  call(){
        return company.quote(travelInfo);
    }
}

//獲得多個公司旅遊報價
public List<TravelQuote> getTravelQuotes(TravelInfo travelInfo, set<TravelCompany> companies, Comparator<TravelQuote> ranking, long outtime, TimeUnit unit) throws InterruptedExecption{
    List<QuoteTask> tasks = new ArrayList<QuoteTask>();  // 任務列表
    for(TravelCompany company : companies){
        tasks.add(new QuoteTask(company, travelInfo));
    }
    
    
    List<Future<TravelQuote>> futures = exec.invokeAll(tasks, outtime, unit);  //執行緒池執行報價任務返回future list
    List<TravelQuote> quotes = new ArrayList<TravelQuote>(tasks.size());
    
    Iterator<QuoteTask> taskIter = tasks.iterator();
    for(Future<TravelQuote> f : futures){
        QuoteTask task = taskIter.next();
        try{
            quotes.add(f.get());
        }catch(ExecutionException e){
            quotes.add(task.getFailureQuote(e.getCause()));
        }catch(CanellationExecption e){
            quotes.add(task.getTimeOutQuote(e));
        }
    }
    
    Collections.sort(quotes, ranking);
    return quotes;

}