http://scarlett.tistory.com/entry/Thread-4%EA%B0%84%EB%8B%A8%ED%95%9C-Thread-%EA%B2%80%EC%83%89-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8


http://scarlett.tistory.com/entry/Thread-2Thread%EC%9D%98-%EC%82%AC%EC%9A%A9%EB%B0%A9%EB%B2%95



이제 Thread를 활용하는 간단한 프로그램을 소개해 보겠습니다.


상당히 많은 정보를 모아놓은 데이터소스를 생각해 보겠습니다.
대표적으로는 정보를 체계적으로 모아 놓은 데이터베이스를 생각할 수 도 있구요.

또는 디렉토리 서버에 저장되어 있는 정보를 생각할 수도 있습니다.

혹은 인터넷에 있는 모든 웹서버의 내용을 데이터소스로 생각해도 되겠습니다.

그리고 이런 정보를 모아놓은 데이터소스가 존재한다고 가정하고 그 데이터소스에 접근이

가능하다고 가정합니다이런 상황에서 여러분이 인기가수 장나라에 대한 정보를

검색하는 프로그램을 만든다고 해보겠습니다.

논의를 간단히 하기 위해 데이터 소스를 유명 연예신문이 운영하는 웹서버의 홈페이지

내용으로 제한하도록 하겠습니다연예신문 웹서버의 홈페이지에 장나라에 대한 기사

가 있는지 검색하고 싶은 것입니다.

물론 검색한 결과를 될 수 있다면 빨리 알고 싶은 것이 당연한 일일 것입니다.

 

이제 이런 가정아래 프로그램을 설계합니다.

첫째연예신문 웹서버의 내용을 찾아 검색하는 일을 담당하는 객체가 필요 합니다.

둘째는 연예신문 웹서버가 하나가 아니고 여러 개이기 때문에 한개의 Thread

웹서버를 순서대로 검색하는 것보다 여러 개의 Thread가 동시에 검색을 수행하는 것이

좋을 것입니다가장 간명하게는 하나의 웹 서버를 하나의 Thread가 검색을 담당하도록

한다면 간단해 질 것 같네요.

 

 프로그램 설계내용을 좀 자세히 살펴보도록 하겠습니다.

프로그램 설계에 따르면 장나라등의 검색하고 싶은 대상을 검색을 하는 일을 담당하는

객체가 필요한데 , 그 객체는 반드시 Runnable객체로 구현해야 됩니다.

왜냐하면 Thread와 함께 사용되어서 동시에 어떤일(검색)을 하고자 하기 때문입니다.

검색하는 일을 담당하는 객체가 Runnable객체가 아니면 Main Thread에서 순차적으로

검색을 수행할 수 밖에 없기 때문입니다검색을 담당하는 객체는 Runnable객체로

만들어 져야합니다, Thread와 함께 사용되어져야 한다면 대부분 Runnable객체를

준비해야만 합니다.

 그 다음 검색을 담당할 객체 즉, Runnable객체는 약간의 정보를 가지고 시작해야만

합니다일단 어디를 검색해야 하는지 알려 주어야하고그 다음은 어떤 것을 검색해야

하는 것을 알려 주어야 합니다그리고 어떤 방법으로 검색해야 하는지 알려주어야 할 것

입니다.

어디를 검색해야하는 문제는 연예정보를 제공하는 웹 서버의 위치인 URL로 정하겠습니다.

자바에는 URL 객체가 있어서 이 URL 객체로부터 InputStream을 얻을 수 있습니다.

일단 InputStream을 얻었다는 것은 자바에서 지원하는 I/O 클래스를 이용해서 정보를 읽을

수 있다는 것입니다, URL객체와 InputStream을 이용하여 디스크에 있는 파일을

읽어내듯이 웹서버의 정보를 자연스럽게 읽어낼 수 있습니다.

어떤 것을 검색해야하는 문제는 Runnable객체의 생성자에 검색할 대상의 정보를 인자로

넘겨 주면 되겠지요간명하게 해결할 수 있습니다.

남은 문제는 어떤 방법으로 검색해야 하는지입니다.

여기서는 간명하게 웹서버의 홈페이지를 한줄씩 읽어가면서 그 읽어낸 정보중에 검색대상이 포함되어 있으면 정보를 찾은 것으로 하겠습니다정보를 찾은 다음에는 그만 빠져나오도록 하겠습니다 "장나라"를 검색하는데 웹 서버 홈페이지에 "장나라텍스트가

포함되어 있다면 정보를 찾은 것으로 한다는 뜻입니다.

 

Runnable 검색기능 객체

 

SearchTask는 검색의 기능을 담당하는 Runnable객체입니다. Runnable객체로 설계한 것은

SearchTask객체를 Thread와 함께 사용하겠다는 뜻을 처음부터 갖고 있는 겁니다.

 

import java.net.*;

import java.io.*;

/**

* URL로 지정되는곳에서 TARGET을 찾습니다.

*/

public class SearchTask implements Runnable {          

                          

    private String url;                                                                           

    private String target;                  

    /**

    * String url    : 검색할 URL

    * String target : 검색할 대상

    */                              

    public SearchTask(String url,String target) {

        this.url = url;

        this.target = target;

    }

   

    public void run() {

        BufferedReader br = null;                                

        try {

        URL u = new URL(url);

            br = new BufferedReader(new InputStreamReader(u.openStream()));                  

            String s = "";

            while((s = br.readLine()) != null) {                                                    

                if (s.indexOf(target) != -1) {                                                        

                    System.out.println(url + " , " + target + " 찾았습니다.");

                    return;                                                                                                          

                }

            }

            System.out.println(url + " , " + target + " 없습니다.");

        }

        catch(Exception e ) {

            e.printStackTrace();

        }

        finally {

            try {

                if (br != null)                        

                    br.close();

            }

            catch(Exception ignore) { }         

        }

    }

}

예제 7 - 13 SearchTask.java

 

예제 7 - 13 SearchTask "implements Runnable"을 보면 알 수 있듯이 Runnable

클래스입니다. SearchTask객체자체가 Thread와 함께 사용될 것은 전제로 하기 때문에

Runnable로 설계하는 것이 옳습니다.

 

SearchTask의 생성자를 살펴보면 두개의 인자를 가지는데 그 첫번째가 검색해야할

웹서버의 URL의 주소를 나타냅니다그 두번째는 검색해야할 대상을 의미합니다.

SearchTask는 가장 일반적인 Runnable객체의 사용법을 보여주고 있는데그것은

다름 아니라 Thread가 수행해야할 내용은 public void run()에 적어두고,

public void run()에서 필요한 정보는 Runnable객체의 생성자를 통해서 전달받는 것입니다.

여기서 SearchTask는 검색할 url 과 검색할 대상을 생성자로부터 전달 받고 있습니다.

public void run() 메소드를 살펴보면이부분은 Thread가 각각 수행을 하는 부분인데

SearchTask 생성자에서 전달받은 정보인 url String을 이용해서 실제 URL객체를 만들고,

URL객체의 openStream()메소드를 호출해서 InputStream을 얻어내고 있습니다.

여기서 만들어진 InputStream으로 읽기를 하면 웹 서버의 URL의 정보를 읽는 셈이됩니다.

SearchTask에는 InputStream으로부터 정보를 읽으려고 BufferedReader 레퍼런스를 이용

했는데이 레퍼런스를 이용한 이유는 BufferedReader클래스에서 정의해 놓은 readLine()

메소드를 이용하고 싶기 때문입니다.

웹서버 홈페이지로부터 정보를 한줄씩 읽고읽은 정보중에 검색대상이 있는지

확인하고 싶기 때문입니다.  이 이유로 readLine()메소드를 사용하고 싶습니다.

예제 7 - 13 SearchTask try - catch - finally 구문을 아주 적절하게 사용하고 있습니다.

 

BufferedReader레퍼런스를 try 블록 바깥에 선언해서 finally구문에서 BufferedReader

레퍼런스를 이용해서 열어놓은 스트림을 반드시 닫고자 하기 때문입니다.

finally 구문은 정상적인 흐름이든예외가 발생하든 반드시 실행이 되는

부분이므로 시스템 자원을 정리하는 곳으로는 그야 말로 안성 맞춤이기 때문입니다.

필자는 사용하는 시스템자원은 반드시 try - catch - finally 구문을 통해서 필요하면

사용하고 , 필요없어지면 정리합니다이런 규칙엔 예외가 없습니다.

 

SearchTask는 약간의 트릭도 사용하는데사용하고 싶은 스트림은 InputStream이 아니라

Reader, 정확하게는 BufferedReader입니다.

하지만 URL객체로부터 openStream()메소드 호출로 얻을 수 있는 것은 InputStream이므로

InputStream을 우선 InputStreamReader를 이용해서 Reader로 변경해야 겠습니다.

그리고 해당 Reader BufferedReader로 덮어씌워서 결국에는 사용하고자하는 궁극적인

스트림 BufferedReader를 이용할 수 있게 되었습니다.

늘 생각하는 것이지만 , 자바에서의 I/O의 설계는 참 멋있다는 생각이 듭니다.

BufferedReader에는 readLine()이라는 메소드가 있고한 줄씩 읽는 메소드입니다.

만약 스트림을 모두 읽어내게되면 null을 돌려주게됩니다그때는 읽기를 그만 두게

되면 되겠지요. while문은 스트림으로부터 한줄씩 읽어내고만약 끝까지 다 읽었다면

(null 을 돌려주었다면읽기를 중단하고 while문을 빠져나라가는 뜻입니다.

SearchTask의 검색대상을 찾았는지 찾지 못했는지 알게되는 여부는 while 문속에 있는

String클래스에 있는 indexOf(String s) 메소드를 이용해서 구현했습니다.

String클래스의 indexOf(String s)메소드는 String s가 포함되어 있다면 그 s가 시작되는 첫번째 위치의 인덱스를 돌려주고만약 s가 포함되지 않았다면 –1을 돌려주게 됩니다.

 웹서버의 홈페이지에서 한줄을 읽고 ,읽은 정보중에 target이 포함되어 있다면

(즉 –1이 아니라면정보를 찾았다고 알리고 (화면에 출력하고정보 찾기를 중단해라

(public void run()을 빠져나가라)는 뜻입니다실행중인 Thread를 중지시키는 가장 좋은

방법은 public void run()을 마치는 일이고, return은 아주 좋은 선택입니다.

메소드에서 return하게되면 해당 메소드를 종료하는 셈이니까요.

 

 

Basic Thread 프로그램

 

SearchScript Thread SearchTask Runnable을 이용해서 Concurent 검색을 수행하고

있습니다.

 

public class BasicConcurrentSearch {

    public static void main(String[] args) {

           if (args.length != 1) {          

                         System.out.println("java -classpath CLASSPATH

BasicConcurrentSearch person");

                         System.exit(1);

           }

           String target = args[0];       

          

        Runnable r1 = new SearchTask("http://www.sportsseoul.com",target);

        new Thread(r1).start();        

 

        Runnable r2 = new SearchTask("http://www.sportschosun.com",target);

        new Thread(r2).start();

 

        Runnable r3 = new SearchTask("http://www.stoo.com",target);

        new Thread(r3).start();

 

        Runnable r4 = new SearchTask("http://www.goodday.com",target);

        new Thread(r4).start();

    }

}

예제 7 - 14 BasicConcurrentSearch.java

 


사용자 삽입 이미지