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