스레드

윤재열·2022년 1월 21일
0

Java

목록 보기
39/71

프로세스(process)란?

프로세스란 단순히 실행 중인 프로그램(program)이라고 할 수 있습니다.
즉, 사용자가 작성한 프로그램이 운영체제에 의해 메모리 공간을 할당받아 실행 중인 것을 말합니다.
이러한 프로세스는 프로그램에 사용되는 데이터와 메모리 등의 자원 그리고 스레드로 구성됩니다.

스레드(thread)란?

스레드(thread)란 프로세스(process) 내에서 실제로 작업을 수행하는 주체를 의미합니다.
모든 프로세스에는 한 개 이상의 스레드가 존재하여 작업을 수행합니다.
또한, 두 개 이상의 스레드를 가지는 프로세스를 멀티스레드 프로세스(multi-threaded process)라고 합니다.

  • 운영체제는 두 가지 이상의 작업을 동시에 처리하는 멀티태스킹을 할 수 있도록 CPU 및 메모리 자원을 프로세스마다 적절히 할당해주고 병렬로 실행시킨다.
    예를 들어, 워드로 문서 작업을 하면서 동시에 윈도우 미디어 플레이어로 음악을 들을 수 있습니다.
  • 멀티태스킹은 멀티스레드를 뜻하는 것은 아니다.
  • 스레드(thread)는 사전적 의미로 한 가닥의 실이라는 뜻인데, 한가지 작업을 실행하기 위해 순차적으로 실행할 코드를 실처럼 이어놓았다고 해서 유래된 이름입니다. 하나의 스레드는 하나의 코드 실행이기 때문에 한 프로세스 내에 스레드가 2개라면 2개의 코드 실행 흐름이 생긴다는 의미이다.
  • 멀티 스레드로 동작하는 메신저의 경우 파일을 전송하는 스레드에서 예외가 발생하면 메신저 프로세스 자체가 종료되므로 채팅스레드도 같이 종료된다.(그렇기 때문에 멀티 스레드에서는 예외 처리를 신경써야 한다.)

메인스레드

자바의 모든 애플리케이션은 메인 스레드가 main()메서드를 실행하면서 시작합니다.
메인 스레드는 main() 메서드의 첫 코드부터 아래로 순차적으로 실행하고, main() 메서드의 마지막 코드를 실행하거나 return문을 만나면 실행이 종료됩니다.

  • 싱글 스레드 어플리케이션에서는 메인 메서드가 종료하면 프로세스도 종료됩니다.
  • 멀티 스레드 어플리케이션에서는 실행 중인 스레드가 하나라도 있다면,프로세스는 종료되지 않습니다.(메인 스레드가 작업 스레드보다 먼저 종료되더라도 작업 스레드가 계속 실행 중이라면 프로세스는 종료 되지 않습니다.)

작업 스레드의 생성과 실행

멀티 스레드로 실행하는 애플리케이션을 개발할면 먼저 몇 개의 작업을 병ㄹ려로 실행할지 결정하고 각 작업별로 스레드를 생성해야 합니다.

  • 어떤 자바 애플리케이션이건 메인 스레드는 반드시 존재하기 때문에 메인 작업 이외에 추가적인 병렬 작업의 수만큼 스레드를 생성하면 됩니다.
  • 자바에서는 작업 스레드도 객체로 생성되기 때문에 클래스가 필요합니다.
  • java.lang.Thread 클래스를 직접 객체화해서 생성해도 되지만, Thread 클래스를 상속해서 하위 클래스는 만들어 생성할 수 있습니다.

Runnable 인터페이스를 구현하는 방법,Thread 클래스를 상속받는 방법

두 방법 모두 스레드를 통해 작업하고 싶은 내용을 run()메서드에 작성하면 됩니다.

package Thread;
class ThreadWithClass extends Thread {
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(getName());  //현재 실행 중인 스레드의 이름을 반환한다.
            try {
                Thread.sleep(10);   //0.01초간 스레드를 멈춘다.
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
class ThreadWithRunnable implements Runnable {
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName());
            //현재 실행중인 스레드의 이름을 반환한다.
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class Thread01{
    public static void main(String[] args) {
        ThreadWithClass thread1 = new ThreadWithClass();    //Thread 클래스를 상속받는 방법
        Thread thread2 = new Thread(new ThreadWithRunnable());   //Runnable인터페이스를 구현하는 방법

        thread1.start();    //스레드의 실행
        thread2.start();    //스레드의 실행
    }
}

위 예제의 실행 결과를 살펴보면, 생성된 스레드가 서로 번걸아가며 실행되고 있는 것을 확인 할 수 있습니다.

  • Thread 클래스를 상속받으면 다른 클래스를 상속받을 수 없으므로, 일반적으로 Runnable인터페이스를 구현하는 방법으로 스레드를 생성합니다.

Runnable 인터페이스는 몸체가 없는 메서드인 run()메서드 단 하나만을 가지는 간단한 인터페이스입니다.

예를 들어 리그오브레전드라는 게임을 생각해보면 블리츠크랭크는 상대를 끌어오는 스킬은 Q스킬이 끝나기 전까지는 움직이거나 다른 모션을 취할수 없다.

즉 스레드를 사용하지 않은 경우에는 한 줄이 완전히 끝난 후에 다음 줄로 넘어가게 된다.

반면에 아리라는 챔피언을 보면 QWER을 순서대로 눌러주었는데 스킬의 완료 여부와 상관 없이 다른 스킬을 계속 수행하는 것이 보인다.

즉 스레드를 사용하면 한번에 여러 동작을 수행할 수 있다는 것이다.

package Thread;

public class Blitzcrank {

    String skill_name;

    public Blitzcrank(String cmd) {
        this.skill_name = cmd;
    }

    public void run(){
        System.out.println("시전한 스킬: "+skill_name);

        for(int i=0; i<=3; i++){
            System.out.println(skill_name +"스킬을 "+i+"초간 시전 중입니다.");
        }
        System.out.println("시전 끝난 스킬 : "+skill_name);
    }

    public static void main(String[] args) {
        String[] cmd =new String[]{"Q","W","E"};    //Q,W,E를 넣고 순서대로 수행할 것

        for(int i=0; i<= cmd.length; i++){
            Blitzcrank b = new Blitzcrank(cmd[i]);  //객체화
            b.run();
        }
    }
}

먼저 스레드를 사용하지 않은 일반 코드인데
String타입의 배열 cmd에 Q,W,E를 넣어주고
Blitzcrank를 개개화 한뒤 run()메서드를 수행해 주니
Q,W,E 순으로 코드가 진행되는 것이 보인다.

package Thread;

public class Ahri extends Thread{
    String skill_name;

    public Ahri(String cmd){
        this.skill_name =cmd;
    }
    public void run(){
        System.out.println("시전할 스킬: "+skill_name);

        for(int i=1; i<=3; i++){
            System.out.println(skill_name+ " 스킬을 " +i+"초간 시전 중입니다.");
        }
        System.out.println("시전 끝난 스킬 : "+skill_name);
    }

    public static void main(String[] args) {
        String[]cmd =new String[]{"Q","W","E"};

        for(int i=0; i<=cmd.length; i++){
            Ahri a = new Ahri(cmd[i]);
            a.start();
        }
    }
}

여기서는 extends Thread를 통해 사용했다.
보면 거의 동일한 코드지만 다른 점은 Thread는 상속받고 start()메서드를 수행 시run()메서드를 수행한다는 차이가 있따.

여기서 실행된 코드를 보면
a.start 는 순서대로 수행하되 run()은 들어오는 순서대로가 아닌 동시에 수행되는 것이 보인다.

  • 쓰레드를 사용하면 위 코드의 수행 완료여부와 상관없이 들어오는 족족 계속 코드를 수행하게 된다.

왜 쓰레드를 사용할까?

  • 쓰레드를 사용하면 동시에 여러개의 코드를 수행할 수 있으므로 동시에 엄청난 양이 들어오는 채팅서비스나 공장의 경우에는 하나씩 처리하면 엄청난 시간이 걸리기 때문에 쓰레드를 사용하여 많은 양도 한번에 처리할 수 있다.

  • 다만 쓰레드를 사용시 주의할 점이자 단점은 쓰레드로 하번에 많은 코드들을 수행할수록 컴퓨터에 부하가 심해지며 쓰레드 수행 도중 내게 필요한 자원을 남이 가지고 있고 남은 남에게 필요한 자원을 내가 가지고 있어서 서로 무한정 대기하는 교착상태( Deadlock) 문제가 있으므로 이에 주의해야한다.

profile
블로그 이전합니다! https://jyyoun1022.tistory.com/

0개의 댓글