public class TokenSearchWork {InterruptedException을 눈 여겨 보자. 두 함수 모두에서 이 예외를 던질 수 있도록 선언되었다. 이것은 검색 쓰레드를 멈추는데 사용된다. 또한 인터럽트 발생을 확인하고 InterruptedException을 생성해 던지는 간단한 private 함수 checkForInterrup()를 만들것이다. 이 함수는 코드를 좀더 간결하게 모으는 용도이다. 이 예외는 검색 쓰레드에 의해 적절히 처리될 것이다. 이제 SearchThread의 run 함수는 새로 만든 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 !!!");
}
}
}
public void run() {run() 함수는 먼저 TokenSearchWork 클래를 생성한다. 그리고 getAllDirectories() 함수를 호출하여 전체 디렉토리의 리스트를 얻는다. 이것은 수행되어야 할 총 작업량을 쓰레드에게 알려준다. 그 다음 디렉토리 전부를 검색 쓰레드가 findFilesInDirectory(..) 호출을 통해 파일에 문자열을 검색한다. 이로서, 검색 쓰레드는 항상 완료된 진행상황을 알 수 있다. 또한 InterruptedException은 for(..) 루프를 빠져나올수 있도록 한다. 완료된 퍼센티지와 취소를 돕기 위해 SearchForm.setTextArea()의 인자가 바뀌었음을 주의해라. 두 개의 새로운 인자가 도입되었다: percentDone 과 작업 완료 여부 확인을 위한 done이다. SearchForm은 아래와 같이 이 변경을 처리한다.
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);
}
public void setTextArea(List javaFiles,이번 iteration에서의 기능은 대부분 시나리오에서 충분할 것이다. 그러나 pause()와 resume() 같은 기능이 필요한 순간이 있을 수 있다. stop() 을 제공했던 것처럼, 자바는 Thread 클래스에서 이 함수들을 제공한다. 그러나 사용상 위험 때문에 이 함수들은 deprecated 되었다. 하지만 우린 이 기능이 필요하므로 스스로 pause()와 resume()을 만들어 보도록 하겠다.
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
}
}
private static final int NORMAL = 0;Pause와 Resume을 위한 새로운 두 개의 함수는 아래와 같이 SearchTread에 추가하자. (synchronized 키워드를 주의하라.):
private static final int PAUSE = 1;
private static final int RESUME = 2;
public synchronized void pauseWork() {새로 추가된 두 함수는 request 변수를 적절히 설정하게 된다. 덧붙여, resumeWork()는 현재 대기중인 SearchThread를 깨우기 위해 notify(..) 함수를 호출한다. 해당 쓰레드를 다시 sleep 상태로 만들기 위해 waitIfPauseRequest를 추가한다 :
request = PAUSE;
notify();
}
public synchronized void resumeWork() {
if (request == PAUSE) {
request = RESUME;
notify();
}
}
private void waitIfPauseRequest()이미 보았듯이, request변수가 PAUSE로 설정되면 쓰레드는 wait함수를 호출한다. Request가 다시 RESUME으로 설정될때까지, 쓰레드는 잠자게 된다. 쓰레드가 인터럽트되면 다시 실행하게 된다. 이 경우는, wait 함수 안에서InterruptedException을 던진 것인데 사용자가 Cancel 버튼을 누른 경우에 해당한다.결과로서 그 쓰레드를 exit 하게 된다. 바로 이 기능이 정확히 우리가 원하는 것이다. 사용자가 Pause또는 Resume 버튼을 클릭하면 프런트 엔드는 단순히 이 함수들을 호출 할 것이다. (해당 버튼들은 적절히 enable/disable됨을 명심하라)
throws InterruptedException {
synchronized (this) {
if (request == PAUSE) {
while (request != RESUME) {
wait();
}
request = NORMAL;
}
}
}
....우리는 start, monitor, pause, resume 그리고 stop 기능이 있는 사용자 친화적인 검색기능을 가지는 어플리케이션 만들었다. 여기서 한가지 Pause와 Resume 기능에 관해 주의해야 할 것은 데이터베이스 트랜잭션 안에서 동작하는 작업을 위해 구현하지 말아야 한다는 사실이다. 이런 작업을 Pause 하는 것은 단순히 트랜잭션 시간을 증가시킬뿐더러 다른 트랜잭션에 관계된 쓰레드들이 작업을 멈추고 기다릴 수 있다는 것이다. 트랜잭션 시간 초과와 같은 명백한 단점과는 별도로 성능 역시 명백히 떨어지게 된다.
} 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);
}
}
....
SSISO Community