2014년 1월 20일 월요일

java concurrency in practice ch6

ch6 Task Execution

6.1 Executing Tasks in Threads
일단 테스크를 논리적으로 분리해라

6.1.1 Executiong Tasks Sequentially

웹서버에서 페이지 렌더링을 구현한다고 생각해보자


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


 위에 소스는 지금 하나의 쓰레드에서 돌기 때문에 엄청 느리다.
  또한 rederText 와 downLoadImg 같은 경우 하나는 cpu 하나는 i/o 를 사용함으로
두개로 분리해서 돌리면 더 좋은 성능을 기대 할 수 있다.

그럼 분리해보자
아래 코드는 콜어블을 사용해서 execute 에 다운 받는 부분을 따로 분리한 코드이다. 일단 다운을 시키고 텍스트를 그린다 문제는 하나의 이미지당 하나의 쓰레드에 다운받게 하면 더 빠르지 않을까?
public class FutureRenderer {
 private final ExecutorService executor = ...;
 
 void renderPage(CharSequence source) {
  final List[ImageInfo] imageInfos = scanForImageInfo(source);
  Callable[List[ImageData]] task =
   new Callable[List[ImageData]]() {
    public List[ImageData] call() {
     List[ImageData] result = new ArrayList[ImageData]();
     for (ImageInfo imageInfo : imageInfos)
      result.add(imageInfo.downloadImage());
     return result;
    }
   };
  Future[List[ImageData]] future = executor.submit(task);
  renderText(source);
  try {
   List[ImageData] imageData = future.get();
   for (ImageData data : imageData)
    renderImage(data);
  } catch (InterruptedException e) {
   // Re-assert the thread's interrupted status
   Thread.currentThread().interrupt();
   // We don't need the result, so cancel the task too
   future.cancel(true);
  } catch (ExecutionException e) {
   throw launderThrowable(e.getCause());
  }
 }
}

우리가 원하는건 여러개의 일을 executor 에게 넘기고 완료된 일을 하나씩 가져오는거다
이때는 CompletionService를 사용하자

블락킹 큐와 + executor 서비스의 조합이다.
여러일을 넘긴다. 일이 완료되면 큐에 저장된다. 없으면 블락이 걸린다.

*concurrent pakage 에서는 - 시간도 0으로 계산한다 즉 일부로 다른 코드 넣을 필요 없다.

*executor 를 공유해서 쓴다고 해도 compleationService에 몇개를 집어넣고 몇개를 꺼내올지 알수 있다 .

public class Render{
 private final ExecutorService executor;

 Render(ExecutorSerivce executor){
  this.executor = executor
 }

 void renderPage(CharSequece source){
  final List[ImgInfo] info = scanForImageInfo(source);
  CompletionService[ImageData] complationService = new ExecutorComplationService[ImageData](executor);
  for(final ImageInfo imagInfo :info)
  compleationService.submit(new Callable[ImageData](){
   public ImageData call(){
    return imageInfo.downLoadImg();
   }
  });

  rederText(soruce);

  try{
   for(int t = 0, n = info.sizE(); t [ n ; t++){
    Future[ImageData] f = complationSerivce.take();
    ImageData imageData = f.get();
    renderImages(imageData);
   }

  }catch (InterruptedException e) {
   // Re-assert the thread's interrupted status
   Thread.currentThread().interrupt();
   // We don't need the result, so cancel the task too
   future.cancel(true);
  } catch (ExecutionException e) {
   throw launderThrowable(e.getCause());
  }



 }
}


이번에는 시간을 생각해보자 예를 들어 화면에 광고를 표시할때 다른 서버에서 데이터를 가지고 오지 못하는걸로 느려질 필요가없다 즉 시간을 정하고 그시간안에 응답이 오지 않으면
디펄트값을 넣고 진행하던 작업을 캔슬한다.

page renderPageWithAd() throws IntrerruptExecption{
 long endNanos = System.nanTime() + TIME_BUDGET;
 Future[ad] f = exec.submit(new FetchTask());
 Page page = rederPageBody();
 Ad ad;
 try{
  long timeLeft = endNanos - System.nanoTime();
  ad = f.get(timeLeft, NANOSECONDS);
 }catch(ExecutionException e) {
  ad = DEFAULT_AD;
 }catch(TiemoutException e{
  ad = DEFAULT_AD;
  f.cancel(true);
 }

 page.setAd(ad);
 return page;
}
1

이번에는 위와 같은게 여러개가 있다고 해보자 각각을 해야 하는가?
아니다 invokeAll 을 사용하면된다 invokeAll 의 경우 시간안에 실패하면 자동으로
future 를 캔슬 해준다.


private Class QuoteTask implements Callble[travelQuote]{
 private final TravelCompany company;
 private final TravelInfo travelInfo;

 public TravelQute call() throws Exception{
  return company.soliciQuote(travelinfo);
 }
}

public List[TravelQuote] getRankedTravelQuotes(
 TavelInfo trableInfo, set[TravelCompany] companies,Comparator[TravelQuote] ranking, long time, timeUnit, unit) 
 throws InterruptedExecption{
  List[QutoeTask] tasks = new ArrayList[QouteTask]();
  for(travelCompany company: companies)
   task.add(new QuoteTask(company, travelInfo);
  
  List[Future[TravelQuotes]] futures = exec. invokeAll(tasks, time, unit);
  List[TravelQuotes] quotes = new ArraylList[TravelQuote](tasks.size());
  Iterator[QuoteTask] taskIter = tasks.iterator();

  for(Future[TravelQuote] f : futures){
   QuoteTask task = taskIter.next();
   try{
    quotes.add(f.get());
   }catch(ExectutionException e){
    qutoes.add(task.getFailreQuote(e.getCauese);
   }catch(CancellationException e){
    quotes.add(task.getTimeoutQuote(e);
   }
  }
 Collections.sort(quotes, ranking);
 return quotes;

 }
1