FileFinder finder = new FileFinder(dirFile); List javaFiles = finder.findFiles(token);여기서 알고리즘을 살펴보는데 시간을 보내지는 않을 것이다. 여러분이 다음 iteration을 진행하기 앞서 이번 iteration의 전체 소스를 직접 살펴보기를 바란다. SearchForm.java의 SearchForm 클래스는 SWING 인터페이스를 통해 화면을 만들고 검색 버튼이 눌리면 결과를 화면에 나타낼 것이다. 생성자에서 windows의 크기를 설정한다. JFrame contentPane에 모든 SWING 컴포넌트를 추가한다. 버튼 클릭을 처리하기 위해 SearchForm은 ActionListener 인터페이스를 구현한다. SWING에서는 일반적으로 Listener 메커니즘을 사용해 화면에 대한 사용자 작업을 처리한다. SearchForm.actionPerformed(..) 함수는 Directory, Search, Cancel 버튼이 클릭된 모든 경우의 기능을 구현한다. Directory 버튼이 클릭되면 JFileChooser를 통해 디렉토리를 선택 할 수 있다. 소스 압축파일을 다운 받아 실제 코드를 확인해라. 검색 버튼이 클릭되면, SearchThread 클래스를 이용하여 별도의 쓰레드 검색이 호출되어 검색 프로세스를 시작한다. 아래에 SearchForm 클래스 코드의 일부가 있다.
public class SearchForm extends JFrame implements ActionListener { private JButton dirButton = new JButton("Choose Directory"); private JButton searchButton = new JButton("Search"); private JTextArea area = new JTextArea(); private JTextField tokenField = new JTextField(""); public SearchForm() { // set the initial size setSize(600, 300); // Exit the application on window close this.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE); ((JPanel) getContentPane()).setBorder( BorderFactory.createEmptyBorder( 5, 5, 5, 5)); .... // add the panels to the frame getContentPane().add("North", northPanel); getContentPane().add("Center", new JScrollPane(area)); getContentPane().add("South", southPanel); } public static void main(String[] args) { SearchForm form = new SearchForm(); form.setVisible(true); } /** * Act on the button click from the user */ public void actionPerformed(ActionEvent e){ // open a file dialog and let the // user choose a file Object source = e.getSource(); if (source == dirButton) { .... } else if (source == searchButton) { .... } } public void setTextArea(List javaFiles) { .... } }SearchThread.java: 검색을 위한 새로운 쓰레드를 만들기 위해 Thread 클래스를 상속받았다. 이름은 아래 볼 수 있듯이 SearchThread이다:
public class SearchThread extends Thread { private File rootDir; private String token; private SearchForm form; public SearchThread(File rootDir, String token, SearchForm form) { this.rootDir = rootDir; this.token = token; this.form = form; } public void run() { FileFinder finder = new FileFinder(rootDir); List javaFiles = finder.findFiles(token); form.setTextArea(javaFiles); } }생성자는 디렉토리와 검색 문자열, SearchForm(화면 결과 출력을 위해)을 인자로 가진다. SearchThread가 FileFinder를 사용한다는 것을 주의하고, 쓰레드 자신은 사용자가 Search버튼이 누른 경우 호출된다:
// Invoke the search on a different Thread File dirFile = new File(dirName); String token = tokenField.getText(); sThread = new SearchThread(dirFile,token,this); sThread.start();sThread 변수는 SearchForm 멤버 변수란 걸 주의해라. 이 쓰레드가 시작되면, SWING의 Event Dispatching 쓰레드는 다른 작업을 자유롭게 수행할 수 있다. 검색 시작 되면 필요한 작업 중 중요한 한가지는 stop 작업이다. 검색 쓰레드가 화면 TextArea에 파일을 보여주기 아래 함수를 호출한다는 것을 주의해라:
form.setTextArea(javaFiles);SWING에서 사용되는 한가지 황금률은 모든(거의 모든) 화면 수정은 SWING Event Dispatcing 쓰레드 안에서 수행되어지는 것을 바란다는 것이다. 하지만 우리는 SearchThread안에서 화면을 수정할 필요가 있는데 이 경우는 소스를 통해 설명하겠다. Form.sextTextArea(..)의 구현을 보도록 하자:
public void setTextArea(List javaFiles) { StringBuffer areaBuffer = new StringBuffer(); Iterator fileIter = javaFiles.iterator(); while (fileIter.hasNext()) { File file = (File) fileIter.next(); areaBuffer.append( file.getAbsolutePath()) .append("n"); } if ("".equals(areaBuffer.toString())){ areaBuffer.append( "No Files Found !!!"); } SwingUtilities.invokeLater( new SetAreaRunner(area, areaBuffer.toString())); }SWING의 장점으로, 화면 갱신 요구를 SwingUtilities.invokeLater를 사용하면 SWING은 그 즉시 화면 갱신 필요성을 알아차린다. setTextArea(..) 함수는 먼저 파일 리스트 문자열을 만들고나서 invokeLater(..)를 사용해 text area 필드를 화면에 설정한다. SwingUtilities.invokeLater(..)의 코드에 의해 우리는 효과적으로 Event Dispatching 쓰레드가 화면 갱신코드를 호출하도록 만든다. invokeLater 함수는 단순히 Runaable 객체를 단순히 Event Dispatcher 큐에 삽입한다. SWING Event Dispatcher 쓰레드는 변화를 감지하고 우리 코드의 run() 메써드를 호출한다. [그림 4]는 사용자가 Search 버튼을 누른 경우 생기는 일에 대한 시퀀스 다이어그램이다.
thread.interrupt();인터럽트된 쓰레드는 자신이 인터럽트가 되었는지 주기적으로 확인해야 한다. 만약 인터럽트 되었다면, 쓰레드는 for(..) 나 while(..) 같은 loop를 빠져 나와 작업을 종료할 것이다. 이런 코드는 개발자에 의해 추가되는 것이지 자동으로 사용 가능한 것은 아니란 것을 주의해라. JDK는 stop() 함수를 쓰레드 클래스에서 제공한다. 그러나 이 함수는 deprecated되었고 사용하는 것은 권장되지 않는다. 우리 코드에서의 구현은 매우 간단하다. 사용자가 Cancel버튼을 누른 경우 아래와 같다.
if (sThread != null) { sThread.interrupt(); }SearchThread 클래스는 간단히 검색에 대한 책임을 FileFinder 클래스에 모두 위임하고 있음을 주의해라. 그러므로 FileFinder 클래스는 인터럽트가 발생된 경우 반드시 이를 알릴 필요가 있다. 어떤 쓰레드가 인터럽트 되었는지 여부는 아래처럼 확인 할 수 있다.
Thread.currentThread().isInterrupted()만약 리턴 값이 참이라면 해당 thread는 인터럽트 된 것이다. 그러므로 SearchThread는 주기적으로 인터럽트 되었는지 여부를 확인하고, 그렇다면 종료해야 한다. 쓰레드의 생존여부와 성능 감소 둘 사이의 균형을 위해 너무 잦은 인터럽트 체크를 하지 않도록 주의해라; 잦은 체크는 쓰레드의 생존여부를 자주 확인하여 인터럽트 처리를 빠르게 할 수 있지만 체크하는 만큼의 성능 감소가 된다. 우리 구현에서는 파일 하나가 검색될 때 마다 체크를 수행한다. [그림 5]는 사용자가 Stop 버튼을 클릭한 경우 발생 하는 일에 대한 시퀀스 다이어그램이다.
이전 글 : Java 리플렉션에 대한 재고(reflection)(2)
다음 글 : 예제로 설명하는 쓰레드 제어하기(2)
최신 콘텐츠