public class TokenSearchWork { private File rootDir; public TokenSearchWork(File rootDir) { this.rootDir = rootDir; } public List getAllDirectories() throws InterruptedException { return findDirs(rootDir); } private List findDirs(File directory) throws InterruptedException { checkForInterrupt(); .... return foundDirs; } .... private void checkForInterrupt() throws InterruptedException { if (Thread.currentThread() .isInterrupted()) { throw new InterruptedException( "Interrupted !!!"); } } }InterruptedException 사용을 주의 싶게 보시기 바랍니다: 두 메소드에서 모두 던지고 있으며 쓰레드를 멈출 때 유용합니다. 중단 요청이 발생할 때 InterruptedException 을 던지는 private 메소드 checkForInterrupt()를 만들었습니다. 이것으로 인해 구현이 다소 단순해 졌습니다. 이 예외는 검색 쓰레드에서 잡힐 것이며 적절하게 처리 될 것입니다. SearchThread의 run 메소드는 이제 TokenSearchWork 클래스를 사용하도록 수정 하겠습니다.
public void run() { List allFiles = new ArrayList(); TokenSearchWork work = new TokenSearchWork(rootDir); int percentDone = 0; try { List allDirs = work.getAllDirectories(); int sizeWork = allDirs.size(); for (int j = 0; j < allDirs.size(); j++) { File directory = (File) allDirs.get(j); allFiles.addAll( work.findFilesInDirectory( directory, token)); percentDone = 100 * (j + 1) / sizeWork; form.setTextArea(allFiles, percentDone, false); } } catch (InterruptedException intExp) { // The Task was interrupted } form.setTextArea(allFiles, percentDone, true); }run() 메소드는 먼저 TokenSearchWork 객체를 생성합니다. 다음으로 getAllDirectories() 메소드를 호출하여 디렉터리의 리스트를 받아 옵니다. 이것으로 쓰레드에게 전체 수행해야 할 작업이 얼만큼인지를 알려줍니다. 그 후, 각각의 디렉터리에서 findFilesInDirectory(..)를 호출하여 파일 내부에 키워드가 존재하는지 검색합니다. 이렇게 함으로써, 검색 쓰레드는 항상 현재까지 수행한 작업의 퍼센티지를 알 수 있습니다. 또한, InterruptedException은 for(..) 문을 빠져나가도록 도와줍니다. SearchForm.setTextArea()의 인자가 완료 한 퍼센티지와 취소한 것을 알기 위해서 변경된 것을 살펴봅시다. 작업이 완료 됐는지 알 수 있도록 percentDone과 boolean 두 개의 새로운 파라미터가 추가 되었습니다. SearchForm은 이런 변경을 다음과 같이 처리합니다:
public void setTextArea(List javaFiles, int percentDone, boolean done) { .... SwingUtilities.invokeLater( new SetAreaRunner(area, areaBuffer .toString(), percentDone, done)); } private class SetAreaRunner implements Runnable { private JTextArea area; private String text; private int percent; private boolean done; public SetAreaRunner(JTextArea area, String text, int percent, boolean done) { this.area = area; this.text = text; this.percent = percent; this.done = done; } public void run() { // Set the UI fields correctly } }이번 단계에서 개선한 기능성으로 대부분의 상황에서 유용하게 사용 수 있습니다. 하지만 가끔 두 개의 기능이 필요할 수 있습니다: pause()와 resume()입니다. stop()과 유사하게 Java는 Thread 클래스에서 이런 메소드들을 제공합니다. 하지만 역시 deprecate 됐기 때문에 사용하기엔 위험 합니다. 따라서 만약에 저런 기능이 필요하다면 pause()와 resume()을 만들어야만 할 것입니다.
private static final int NORMAL = 0; private static final int PAUSE = 1; private static final int RESUME = 2;일시 정지와 재 시작을 위한 두 개의 새로운 메소드를 SearchThread에 다음과 같이 추가합니다(synchronized 키워드를 사용한 것을 주의 깊게 보시기 바랍니다.):
public synchronized void pauseWork() { request = PAUSE; notify(); } public synchronized void resumeWork() { if (request == PAUSE) { request = RESUME; notify(); } }위에 있는 두 개의 메소드는 request 변수를 적절한 값으로 설정하고 있습니다. 게다가 resumeWork()는 대기 상태에 있는 SearchThread를 깨우기 위해 notify() 메소드를 사용합니다. 쓰레드를 sleep 상태로 만들기 위해서 새로운 메소드인 waitIfPauseRequest를 추가합니다.
private void waitIfPauseRequest() throws InterruptedException { synchronized (this) { if (request == PAUSE) { while (request != RESUME) { wait(); } request = NORMAL; } } }위에 보이듯이, 쓰레드는 request 변수가 PAUSE값으로 설정되어 있다면 wait(..) 메소드를 호출할 것입니다. Request가 RESUME으로 설정 될 때까지 쓰레드는 sleep 상태가 됩니다. 유일한 예외 상황은 예외(Exception)가 발생할 때입니다. 그런 경우로는, wait(..) 메소드에서 InterruptedException이 전달 됐을 때가 있습니다. 사용자가 취소 버튼을 클릭했을 때 발생하게 되고 그 결과 쓰레드는 종료하게 됩니다. 이게 바로 저희가 원하던 기능입니다. 화면 쪽에서는 간단하게 사용자가 일시 정지나 재 시작 버튼을 클릭했을 때 이 메소드들을 호출 할 것입니다(버튼들은 적절하게 사용가능/불가능 상태로 변하게 됩니다.)
.... } else if (source == pauseButton) { if (sThread != null) { sThread.pauseWork(); pauseButton.setEnabled(false); resumeButton.setEnabled(true); } } else if (source == resumeButton) { if (sThread != null) { sThread.resumeWork(); pauseButton.setEnabled(true); resumeButton.setEnabled(false); } } ....시작, 모니터링, 일시 정지, 재 시작 그리고 멈춤 기능을 갖춘 사용자 중심의 검색 기능을 구현했습니다. 일시 정지와 재 시작 기능에서 주의해야 할 사실은 데이터베이스 트랜잭션 내부에서 동작하는 작업을 위해 구현해서는 안 된다는 것입니다. 일시 정지 같은 기능은 트랜잭션 시간을 증가 시키고 다른 쓰레드들이 지연됩니다. 트랜잭션 타임아웃과 같은 눈에 띄는 결점들은 제쳐 놓는다 하더라도, 성능이 현저히 저하될 것입니다.
이전 글 : 예제로 살펴보는 쓰레드 제어하기 - (1)
다음 글 : MySQL에서의 랭킹 데이터 최적화 - (1)
최신 콘텐츠