본문 바로가기

백엔드/자바

자바 - Thread and Synchronization (배경, Thread의 기본)

반응형

1. 배경


 

1-1. 프로세스


- 하나의 프로그램 혹은 하나의 프로세스에 해당

- 프로세스의 경우에는 자동으로 운영체제가 관리하기 때문에 완벽하게 멀티 태스킹의 지원을 받을 수 있습니다.  프로그램을 만들고 실행만 시킨다면 운영체제 차원에서 CPU의 시분할(Time Sharing)을 지원받을 수 있습니다. 

 

 

 

 

1-2. 멀티태스킹


- 프로세스(Process)의 경우 운영체제에서 자동으로 관리해 준다.
- 운영체제 차원의 프로세스(Process) 관리를 멀티 태스킹(Mutitasking)이라고 한다.

 

 

 

1-3. 프로세스와 스레드


- 하나의 프로세스(Process) 내에는 여러 개의 스레드(Thread)가 존재할 수 있다.

- 우리가 목표로 하는 것은 프로그램 단위의 멀티 태스킹이 아니라 하나의 프로그램 내에서 실행되는 멀티 메서드입니다. 즉 하나의 프로세스 내에 여러 개의 메서드를 동시에 실행하기를 원하는 것입니다. 스레드(Thread)란 바로 하나의 프로세스 내에서 실행되는 메서드를 의미합니다. 일반적인 프로세스의 경우 한순간에 하나의 메서드만이 실행되지만, 스레드의 기법을 이용하면 여러 개의 메서드를 동시에 실행시킬 수 있습니다.

 

 

 

1-4. 스레드란?


-  하나의 프로그램 내에서 실행되는 메서드
- 같은 순간에 두 개의 메서드가 동시에 실행되면 두 개의 스레드가 동작하는 것이다.

- 의미상으로 하나의 스레드는 하나의 메서드에 해당한다고 했지만 실제 프로그램에서도 그러합니다. 단, 조건이 있습니다. 하나의 스레드로서의 조건을 만족하기 위해서는 프로그램 내에서 메서드가 다른 메서드와 동시에 실행되어야 합니다. 즉 하나의 독립된 메서드로 실행되면 그것은 스레드라고 말할 수 있습니다. 이러한 스레드 프로그램을 할 때 주의해야 할 사항이 있습니다. 

 

 

 

1-5. 스레드 프로그래밍시 주의사항


 

◈ 우선권(Priority)
◈ 동기화(Synchronization)

- 동시에 두 개 또는 여러 개의 메서드가 실행되기 때문에 어느 메서드에게 작업할 권한을 많이 줄 것인 지에 대해서 생각해 보아야 합니다. 즉 우선권을 어떤 메서드에게 많이 줄 것인지에 관한 것입니다. 

-  두 개의 메서드가 동시에 작업을 진행할 때 둘 다 하나의 자원을 상대로 작업을 하면 미세한 문제가 발생합니다. 이 때 고려해야 할 것이 자원의 공유입니다.

- 가령, 은행 계좌에 돈이 10000원 있으며, 이 계좌를 A와 B라는 두사람이 공동으로 이용한다고 가정하죠. 그런데 공교롭게도 A와 B가 서로 다른 장소에서 A는 돈을 인출하려고 하고 B는 돈을 입금하려 한다면 무슨 문제가 발생할까요?
  A와 B가 거의 동시에 작업을 진행한다면 은행 시스템은 A나 B 둘 중 한사람을 먼저 처리한 후에 다른 사람을 처리해야 올바른 작업이 이루어질 것입니다. 만약 이것을 지키지 않는다면 아주 심각한 문제가 발생합니다. A와 B가 둘 다 동시에 은행 계좌를 읽어가고 작업을 했다면 둘 다 10000원으로 작업을 할 수도 있습니다. 이렇게 되면 계좌의 돈계산은 엉망이 될 것입니다.

   A가 먼저하든지 B가 먼저하든지 순서를 지키면서 작업을 해야 합니다. 이렇게 하나의 자원을 상대로 순서대로 작업이 처리되는 것을 보장하는 기법을 동기화(Synchronization)라고 합니다.

 

 

 

 

1-6. 동기화의 보장


 

-  공유자원을 상대로 순서대로 작업이 이루어지는 것을 '동기화(Synchronization)가 보장된다'라고 말한다.

- 위의 A와 B는 스레드에 해당하며, 은행 계좌는 공유자원에 해당합니다. 공유자원을 사용할 때 동기화를 지키지 않으면 프로그램에서 예상치 못한 결과를 초래할 수도 있습니다. 이러한 측면에서 이 장에서는 스레드 프로그램을 하는 방법과 우선권 처리, 동기화 기법에 관련된 사항들을 학습하게 될 것입니다.

 

 

 

 

 

 

 

 

2. 스레드의 기본


2-1. 스레드란


- 시퀀셜(Sequential)하게 동작한다는 것은, 
한 순간에 하나의 메서드가 동작하는 것을 의미한다.

- 스레드(Thread)의 기본 목표는 두 개의 메서드를 동시에 실행하는 것

- 한순간에 두 개의 메서드가 동시에 실행되었을 때, 실행된 메서드들을 스레드(Thread)라고 한다.

 대부분의 프로그램 언어에서 동시에 두 개의 메서드를 실행하기 위한 스레드 라이브러리를 제공하고 있습니다. 이것은 자바 또한 마찬가지입니다. 이쯤에서 우리는 스레드의 정의를 내리고 가도록 하죠.

- 스레드란? :  프로그램에서 독립적으로 실행되는 메서드

 

2-2. Runnable로 스레드 만들기


- 중요한 것은 '스레드는 메서드다'라는 말을 꼭 기억하시기 바랍니다. 

- 스레드로 사용할 메서드를 만들어 볼 것이고, 먼저 인터페이스를 만들겠습니다. 

 스레드의 메서드를 구현하기 위한 인터페이스
 public interface Runnable{
         void run();
 }

 

- 이를 사용하기 위해 구현하겠습니다. 

 Runnable 인터페이스의 구현
 public class Top implements Runnable{
         pubic void run(){
                 //...작업 내용
         }
 }




- 이제 이것을 스레드화해야 합니다.  스레드에 끼워 넣어서(장착시켜서) 스레드를 동작시킵니다.

 

 Runnable 인터페이스를 구현하고 있는 Top형의 객체 생성
 Top t = new Top();



- 진짜 스레드는 Thread 클래스로 만들어야 합니다. Thread를 만들 때 Runnable을 구현한 Top형의 객체를 넣어주고(장착하고) 만들면, 내부에서 Runnable의 run() 메서드를 이용해서 스레드를 만들게 됩니다. 다음과 같이 Thread형의 객체를 만들 때 생성자의 매개변수로 Runnable을 구현한 객체를 넣어주기만 하면 됩니다.

 진짜 스레드에 Runnable 장착
 Top t = new Top();
 Thread thd = new Thread(t);



- 이제 run()을 동작시켜야겠죠. Thread의 start()를 호출하면 내부에서 run()이 독립적으로 호출됩니다.

 

스레드 동작시키기
 Topt t = new Top(); //가짜 스레드
 Thread thd = new Thread(t); //진짜 스레드
 thd.start(); //스레드 동작시키기



- 이것을 프로그램으로 작성하면 다음과 같습니다.

/**
Runnable을 구현해서 만드는 스레드(스레드를 생성하는 방법)
**/
class Top implements Runnable{
    public void run(){
        for(int i=0; i<50; i++)
            System.out.print(i+"\t");
    }
} //end of Top class

public class ThreadMain{
    public static void main(String[] args){
        System.out.println("프로그램 시작");
        Top t = new Top();
        Thread thd = new Thread(t);
        thd.start();
        System.out.println("프로그램 종료");
    } //end of main
} //end of ThreadMain class

- 출력 결과

 

/***
C:\javasrc\chap08>javac ThreadMain.java
C:\javasrc\chap08>java ThreadMain
프로그램 시작
프로그램 종료
0       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      41      42      43      44      45      46      47      48      49
***/
───────────────────────────────────────ⓑ



- 프로그램의 결과를 보시면 이상한 것을 발견할 수 있습니다. '프로그램 시작'과 '프로그램 종료'가 출력되고 난 뒤에 스레드의 작업이 진행되는 것입니다.

 >> main()이 작업을 끝낸 후 동작중인 스레드가 작업을 끝낼 때까지 대기하기 때문입니다. 이러한 원리는 다음과 같이 표현할 수 있습니다.


- main()도 하나의 스레드로써 동작하다가 다른 스레드를 동작시키고 있는 것입니다. 그리고 start()하는 순간 동시에 작업이 이루지고 있는 것입니다. 그렇기 때문에 '프로그램 종료' 메시지가 먼저 출력이 된 것입니다.

- 결과적으로 보면 run()이라는 메서드를 동시에 하나 더 실행시키는 기법입니다.

- 스레드를 하나 더 띄워 보도록 하죠. 다음과 같이 한다면 똑같은 메서드에 해당하는 두 개의 스레드가 동작하는 것입니다. 즉 독립적으로 메서드를 두 개 만들어서 띄우는 것이 됩니다.

/**
두 개의 스레드를 생성한 후 실행하는 예(Runnable 구현)
**/
class Top implements Runnable{
    public void run(){
        for(int i=0; i<50; i++)
            System.out.print(i+"\t");
    }
} //end of Top class

public class ThreadMain2{
    public static void main(String[] args){
        System.out.println("프로그램 시작");
        //1. Runnable을 구현하는 객체 만들기
        Top t = new Top();
        //2. Runnable을 장착한 후 진짜 스레드 만들기
        Thread thd1 = new Thread(t);
        Thread thd2 = new Thread(t);
        //3. 스레드 동작 시키기
        thd1.start();
        thd2.start();
        System.out.println("프로그램 종료");
    } //end of main
} //end of ThreadMain2 class
/***
C:\javasrc\chap08>javac ThreadMain2.java
C:\javasrc\chap08>java ThreadMain2
프로그램 시작
프로그램 종료
0       1       2       3       4       5       6       7       8       9
10      11      12      13      14      15      0       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      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      41      42      43      44      45      46
47      48      49      33      34      35      36      37      38      39
40      41      42      43      44      45      46      47      48      49
***/



- 위의 결과를 보시면 번갈아가면서 숫자가 출력되는 것을 확인할 수 있을 것입니다. main()과 Runnable 2개를 합쳐서 전체 3개의 스레드가 동작하고 있는 것입니다. 위의 예제는 다음과 같은 방식으로 동작합니다.


- 위의 예제에서 다음과 같이 하나의 t를 이용해서 2개의 스레드를 만들고 있습니다. 

Top t = new Top();
Thread thd1 = new Thread(t);
Thread thd2 = new Thread(t);



** 참고
-  C나 C++에서는 스레드를 구현하기 위해서 함수 포인터(Function Pointer)라는 것을 이용합니다. 하지만 자바에서는 함수 포인터의 개념이 없기 때문에 Runnable 인터페이스를 이용하는 것입니다. 

2-3. Thread 상속으로 스레드 만들기


 

2-4. Runnable을 사용하는 이유


 

2-5. 스레드의 상태


 

2-6. 결론


 

 

 

 

 

반응형

'백엔드 > 자바' 카테고리의 다른 글

자바 - 클래스와 객체  (1) 2023.04.18
자바 - Stream, 표준 스트림과 File 클래스  (1) 2022.10.05
자바 기초  (0) 2022.08.29
자바 - 클래스  (0) 2022.08.25
자바 클래스의 개념  (0) 2022.08.24