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