티스토리 뷰

  스레드 실행은 run() 과 start() 메소드를 활용한다. 하지만, 이 두 메소드는 값을 반환하지 않는다. 스레드로 부터 값을 반환 받기 위해서는 다른 방법을 사용해야 한다. 


1. 스레드의 결과 출력


  우리가 숫자를 입력하면 그것을 일련의 복호화 과정을 거쳐 문자열로 만드는 스레드가 존재한다고 생각해보자. 예를 들어 우리가 계좌번호를 입력해야 하는데 그냥 숫자로 데이터베이스에 전송된다면, 보안 문제가 발생할지도 모른다. 그래서 우리는 이 문제를 해결하기 위한 복호화 프로그램을 만들기로 했다.


  암호화 방법은 아주 단순하게 Integer 계좌번호를 입력 받아서 각각의 숫자를 문자열로 변환하는 방법을 사용하고자 한다. 프로그램을 아래와 같이 작성할 수 있을 것이다.


MakeString.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class MakeString extends Thread {
 
    private int account_no;
    private String account_code;
    
    public char[] Sarray = {'/','*','-',')','(','%','$','!','^','?'};
    
    public MakeString(int account){
        this.account_no = account;
    }
    
    @Override
    public void run(){
        try{
            // int to String
            char[] temp = Integer.toString(account_no).toCharArray(); 
            account_code ="";
            for(int i=0; i<temp.length;i++){
                int index = (int)temp[i]-48;
                account_code = account_code + Sarray[index];
            }
            
            System.out.println("스레드 종료");
        } catch (Exception e) {
            System.err.println(e);
        }
        
    }
    
    public String getString(){
        
        return account_code;
    }
    
}
cs



Main.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.util.Scanner;
 
public class Solution {
 
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        
        System.out.println("계좌 번호를 입력하세요.");
        
        Scanner sc = new Scanner(System.in);
        int account = sc.nextInt();
        
        // 계좌번호 입력
        MakeString ms = new MakeString(account);
        ms.start();
        
        // 값 출력
        String accountCode = ms.getString();
        System.out.println("계좌번호 암호화 :"+accountCode);
    }
 
}
 
cs



 다음의 프로그램을 실행하면 아래와 같은 결과를 얻을 수 있다.




암호화가 진행되지 않아 null 값이 입력되었다. 그 원인은 쓰레드 종료 보다 먼저 프로그램이 돌아서 값을 받아오지 못한 것이 원인이다. 만약 아래와 같이 메인 프로그램에서의 지연을 발생시키면 원하는 결과를 얻을 수 있다.


Main.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import java.util.Scanner;
 
public class Solution {
 
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        
        System.out.println("계좌 번호를 입력하세요.");
        
        Scanner sc = new Scanner(System.in);
        int account = sc.nextInt();
        
        // 계좌번호 입력
        MakeString ms = new MakeString(account);
        ms.start();
        
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
        // 값 출력
        String accountCode = ms.getString();
        System.out.println("계좌번호 암호화 :"+accountCode);
    }
 
}
cs




  스레드는 CPU의 상태 혹은 JVM의 작동에 따라 각각 스레드에 할당 되는 시간이 달라질 수 있다. 이를 경쟁 조건 (Race Condition) 이라고 한다. 그래서 이런 경쟁 조건 문제를 해결하기 위해 스레드에 대한 호출을 할 때, 반환 값이 준비될 떄 까지 프로그램을 잠시 미루거나 다른 행동을 요구 할 수 있어야 한다.



2. 폴링 (Polling)


  가장 단순하게 이 문제를 해결하는 방법은 폴링이다. 폴링 이란 스레드에 플래그를 놓아 값이 준비된다면 플래그를 메인 프로그램에 전달해 주는 것이다. 메인 프로그램은 주기적으로 스레드에 값이 준비 되었는 지 물어보는 작업이 필요하다.

  폴링을 이해하기 위해 이번엔 계좌번호를 1개가 아닌 다수의 계좌번호를 복호화 한다고 가정하자. 그래야 멀티 스레드 환경을 구성할 수 있기 때문이다.

  


  위와 같이 프로그램이 실행 된 이후 스레드에 값이 입력 되었는지 계속 물어보는 작업이다. Polling을 구현하기 위한 플래그로 finflag 변수를 boolean 형으로 추가 했다. 이 과정을 구현하면 다음과 같다.


MakeString.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 
public class MakeString extends Thread {
 
    private int account_no;
    private String account_code;
    protected boolean finflag; // Polling flag
    
    public char[] Sarray = {'/','*','-',')','(','%','$','!','^','?'};
    
    public MakeString(int account){
        this.account_no = account;
    }
    
    @Override
    public void run(){
        try{
            // int to String
            char[] temp = Integer.toString(account_no).toCharArray(); 
            account_code ="";
            for(int i=0; i<temp.length;i++){
                int index = (int)temp[i]-48;
                account_code = account_code + Sarray[index];
            }
            finflag = true;
            
        } catch (Exception e) {
            System.err.println(e);
        }
        
    }
    
    public String getString(){
        
        return account_code;
    }
    
}
 
cs


Main.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import java.util.Scanner;
 
public class Solution {
 
    public static void main(String[] args) {
        // TODO Auto-generated method stub
 
        Scanner sc = new Scanner(System.in);
        
        System.out.println("입력할 계좌번호의 개수를 입력하세요.");
        int count = sc.nextInt();
        MakeString[] ms = new MakeString[count]; 
        
        for(int j=0; j<count; j++){
            System.out.println("계좌 번호를 입력하세요.");
            int account = sc.nextInt();
            ms[j] = new MakeString(account);
            ms[j].start();
            
        }
        
        // Polling
        for(int i=0; i<count; i++){
            while(true){
                if(ms[i].finflag){
                    String accountCode = ms[i].getString();
                    System.out.println((i+1)+"번 계좌번호 암호화 :"+accountCode);
                    break;
                }
                
            }
        }
    }
 
}
 
cs





  폴링 방식은 쓰레드에 플래그를 두어 쓰레드의 종료 방법을 알 수 있는 매우 간단한 방법이다. 게다가 코딩 작업도 그리 어렵지 않게 구현할 수 있기에 처음 쓰레드를 사용하는 개발자에게는 좋은 방법이다. 하지만, While(true) 문은 항상 위험 성이 존재한다. 프로그램을 쉽게 무한 루프에 빠지게 만드는 주요 원인이다. 위와 같은 작업은 우리에게 불필요한 작업을 너무 많이 요구하게 된다.


  그리고 매우 낮을 확률로 발생하지만, 뒷부분 쓰레드는 실행이 안될 수도 있다. 앞선 쓰레드가 너무 빨리 종료하게 된다면은 Polling 부분에서 더이상 쓰레드가 남아 있지 않는다고 판단해 프로그램을 종료시키는 경우도 발생한다. 아마 위 프로그램의 입력 계좌번호 수를 크게 하고 앞부분의 계좌번호를 짧게 작성하면 앞서 언급한 문제가 발생할 수도 있다. 이는 프로그램에 치명적인 오류를 가져오게 된다.



3. 콜백 (Callback)

  폴링의 문제점 2가지는 다음과 같다.
  • 메인 프로그램에서 쓰레드에 작업이 종료 되었는지 지속적으로 물어봐야 한다는 점
  • 너무 빠른 쓰레드 종료로 메인 프로그램에서 쓰레드가 더이상 존재하지 않는 다고 판단 할 수 있다는 점
  이를 해결하기 위해 스레드가 직접 메인 클래스에게 자신이 종료 되었다는 사실을 알려주면 된다. 이를 위해서 스레드가 메인 클래스의 메소드를 호출하는 과정이 필요하다. 스레드가 작업이 끝났을 때 자신을 생성한 클래스를 다시 호출하는 방식인데 이를 콜백(Callback)이라고 부른다.


  메인 프로그램에 메소드 하나를 새로 생성 해야 한다. 이 메소드는 쓰레드에서 사용 할 것이다. 그리고 이 메소드가 실행되었다는 것은 쓰레드의 종료를 의미해야한다. 그래야 메인 프로그램에서 쓰레드 종료 여부를 예측 할 수 있다.


MakeString.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
 
public class MakeString extends Thread {
 
    private int account_no;
    private String account_code;
    private int number;
    
    public char[] Sarray = {'/','*','-',')','(','%','$','!','^','?'};
    
    public MakeString(int num, int account){
        this.number = num;
        this.account_no = account;
    }
    
    @Override
    public void run(){
        try{
            // int to String
            char[] temp = Integer.toString(account_no).toCharArray(); 
            account_code ="";
            for(int i=0; i<temp.length;i++){
                int index = (int)temp[i]-48;
                account_code = account_code + Sarray[index];
            }
            // Callback
            Solution.ThreadInterface(number, account_code);
            
        } catch (Exception e) {
            System.err.println(e);
        }
        
    }
    
    public String getString(){
        
        return account_code;
    }
    
}
 
cs

Main.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.util.Scanner;
 
public class Solution {
 
    // Callback Method
    public static void ThreadInterface(int i, String accountCode){
        System.out.println(i+"번 계좌번호 암호화 :"+accountCode);
    }
    
    public static void main(String[] args) {
        // TODO Auto-generated method stub
 
        Scanner sc = new Scanner(System.in);
        
        System.out.println("입력할 계좌번호의 개수를 입력하세요.");
        int count = sc.nextInt();
        MakeString[] ms = new MakeString[count]; 
        
        for(int j=0; j<count; j++){
            System.out.println("계좌 번호를 입력하세요.");
            int account = sc.nextInt();
            ms[j] = new MakeString(j+1,account);
            ms[j].start();
            
        }
        
        
    }
 
}
 
cs





  아까와 동일한 결과값을 번거로운 폴링 작업 없이 노출 시킬 수 있다.


  눈치가 빠른 사람들은 벌써 눈치를 챘겠지만, 폴링방식에서는 입력이 전부 끝난 뒤 폴링 로직이 실행되기 때문에, 모든 입력이 끝난 뒤 암호화 된 결과값이 출력된다. 하지만, 콜백 방식에서는 스레드가 끝나게 되고 바로 값을 노출 시키게 되므로, 입력 한 직후 스레드가 실행되고 결과 값도 노출이 바로 나오게 된다.


  만일 폴링에서 처럼 입력을 전부 받고나서 결과 값의 출력을 원한다면, 메인 클래스에 있는 Callback 메소드에 ArrayList나 Queue를 선언하여 값을 저장한 뒤 값을 출력하는 방식을 채택하면 된다.


  즉, 폴링과 콜백 방식의 차이는 아래와 같이 정리 될 수 있다. 




'Skill > Programming - Network' 카테고리의 다른 글

8.인터넷 주소 클래스 (InetAddress Class)  (0) 2019.06.02
7. 스레드 동기화  (0) 2019.05.19
5. 스레드(Thread)  (0) 2019.04.27
4. 프로세스 관리 (Process Control)  (1) 2019.04.07
3. 프로세스(Process)  (0) 2019.03.27
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함