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 함수 checkForInterrup()를 만들것이다. 이 함수는 코드를 좀더 간결하게 모으는 용도이다. 이 예외는 검색 쓰레드에 의해 적절히 처리될 것이다. 이제 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 과 작업 완료 여부 확인을 위한 done이다. 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 } }이번 iteration에서의 기능은 대부분 시나리오에서 충분할 것이다. 그러나 pause()와 resume() 같은 기능이 필요한 순간이 있을 수 있다. stop() 을 제공했던 것처럼, 자바는 Thread 클래스에서 이 함수들을 제공한다. 그러나 사용상 위험 때문에 이 함수들은 deprecated 되었다. 하지만 우린 이 기능이 필요하므로 스스로 pause()와 resume()을 만들어 보도록 하겠다.
private static final int NORMAL = 0; private static final int PAUSE = 1; private static final int RESUME = 2;Pause와 Resume을 위한 새로운 두 개의 함수는 아래와 같이 SearchTread에 추가하자. (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으로 설정될때까지, 쓰레드는 잠자게 된다. 쓰레드가 인터럽트되면 다시 실행하게 된다. 이 경우는, wait 함수 안에서InterruptedException을 던진 것인데 사용자가 Cancel 버튼을 누른 경우에 해당한다.결과로서 그 쓰레드를 exit 하게 된다. 바로 이 기능이 정확히 우리가 원하는 것이다. 사용자가 Pause또는 Resume 버튼을 클릭하면 프런트 엔드는 단순히 이 함수들을 호출 할 것이다. (해당 버튼들은 적절히 enable/disable됨을 명심하라)
.... } 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); } } ....우리는 start, monitor, pause, resume 그리고 stop 기능이 있는 사용자 친화적인 검색기능을 가지는 어플리케이션 만들었다. 여기서 한가지 Pause와 Resume 기능에 관해 주의해야 할 것은 데이터베이스 트랜잭션 안에서 동작하는 작업을 위해 구현하지 말아야 한다는 사실이다. 이런 작업을 Pause 하는 것은 단순히 트랜잭션 시간을 증가시킬뿐더러 다른 트랜잭션에 관계된 쓰레드들이 작업을 멈추고 기다릴 수 있다는 것이다. 트랜잭션 시간 초과와 같은 명백한 단점과는 별도로 성능 역시 명백히 떨어지게 된다.
이전 글 : 예제로 설명하는 쓰레드 제어하기(1)
최신 콘텐츠