프로세스(process)와 스레드(thread)의 코드

하히호호·2024년 4월 19일
0

✏️ 프로세스(process)란?

프로세스란 단순히 실행 중인 프로그램(program)을 말한다.
프로그램은 코드의 모음이다. 특정 작업을 수행하는 일련의 명령어들의 모음을 의미한다.
사용자가 프로그램을 실행하여 운영체제에 의해 메모리 공간을 할당받아 실행 중인 프로그램을 말한다. 즉, 프로그램이 돌아가고 있는 상태, 작업중인 상태의 프로그램을 의미한다.
프로세스는 프로그램에 사용되는 데이터와 메모리 등의 자원 그리고 스레드로 구성된다.

프로세스의 한계

오늘날 컴퓨터를 생각해보자. 파일을 다운받으며 유튜브 영상을 보거나 노래를 받는다. 멀티 작업이 가능하다는 것이다.
과거의 컴퓨터를 생각해보면 파일을 다운받으면 실행 시작부터 실행 끝까지 프로세스 하나만 사용했다. 즉, 파일이 완료될때까지 하루종일 기다려야했다.

프로그램을 여러 개의 프로세스로 만들게 되잖아?

맞다. 가능은 하다. 하지만 프로그램이 메모리로 올라오면서 차지하는 메모리가 있고 CPU에서 할당받는 자원이 중복될 것이다.

process 단위 실행 코드

실제로 이렇게 사용하진 않지만 계산기를 실행시켜보는 코드를 짜보자.

public static void main(String[] args)  { 
	try {
		//process 단위의 실행 (하나의 작업단위)
		Process p1 = Runtime.getRuntime().exec("calc.exe");
	} catch (Exception e) {
			System.out.println("err : "+ e);
    }
}

Runtime

Runtime클래스의 getRuntime()메서드가 있다.
해당 클래스에 들어가 설명을 읽어보자.


Every Java application has a single instance of class

  • {@code Runtime} that allows the application to interface with
  • the environment in which the application is running. The current
  • runtime can be obtained from the {@code getRuntime} method.
  • An application cannot create its own instance of this class.
    ....

Returns the runtime object associated with the current Java application.
✨해석
현재 Java 응용 프로그램과 연결된 런타임 개체를 반환합니다.

exec()

Executes the specified string command in a separate process.
✨해석
지정한 문자열 명령을 별도의 프로세스에서 실행합니다.


🪄위와 같이 프로세스 하나를 실행할 수 있다.
하나의 프로세스에 하나의 스레드를 가지고 있으면 싱글스레드라고 한다.
싱글 스레드의 경우, 코드는 순차적으로 진행한다.
즉, cpu가 연산작업을 안하는 idle time이 발생한다.

✏️스레드(thread)란?

스레드(thread)란 프로세스(process)내에서 실제로 작업하는 주체를 의미한다.
모든 프로세스는 한개 이상의 스레드가 존재한다. 메인 스레드가 돌아가야 프로그램이 돌아가기 때문이다.
응용 프로그램의 실행은 실행단위(thread)가 담당한다.
두개 이상의 스레드를 가지는 프로세스를 멀티스레드 프로세스(multi-threaded process)라고 한다.
multi thread에 의한 multi tasking이 가능하다.

  • Code, Data, Heap 영역을 공유한다.
  • 각 Thread에 Stack은 따로 할당된다.

프로세스 내의 주소 공간이나 자원을 같은 프로세스 내에 스레드끼리 공유하면서 실행한다.
다른 프로세스의 메모리는 직접 접근할 수 없어 IPC를 이용해 접근해야한다.
한 스레드가 프로세스 자원을 변경하면 다른 스레드(Sibling thread)도 그 변경 사항을 즉시 공유받을 수 있다.

스레드의 생성

스레드를 생성하는 방법에는 크게 2가지 방법이 있다.
스레드 기능을 사용하기 위해서는 run()메서드를 오버라이딩해야한다.
run()메서드는 Runnable에 구현되어있는데 해당 클래스를 열어보자.

public interface Runnable {
	public abstract void run();
}
  • extends Thread
    : Thread 클래스는 Runnable을 구현하고 있는 클래스이다. 이 클래스는 마치 adapter와 같은 역할을 한다.
  • implements Runnable
    : Runnable를 구현받는 것을 의미한다.

1. extends Thread 방법

public class ThreadTest1 extends Thread{
	public ThreadTest1(){
		
	}
	public ThreadTest1(String name) {
		super(name);
	}
    
    public void run() { 
		for(int i = 1; i <=10;i++) {
			System.out.print(i+" : "+getName()+" ");
			
		}
	}
    public static void main(String[] args)  {
    	try {
    		ThreadTest1 th1 =new ThreadTest1("one");
			th1.start();
            Ex37ThreadTest1 th2 =new Ex37ThreadTest1("two");
			th2.start();
            System.out.println();
			System.out.println("프로그램 종료");
		} catch (Exception e) {
			System.out.println("err : "+ e);
		}
	}
} 

출력 결과
프로그램 종료
1: one 2: one 1: two 3: one 2: two 3: two 4: one 4: two 5: one 5: two 6: two 7: two 8: two 9: two 10: two 6: one 7: one 8: one 9: one 10: one

java에서는 단일 상속을 원칙으로 하기때문에 extends를 키워드를 아껴서 사용해야한다.
만약 코드를 짜다가 다른 클래스를 상속받아야하는 경우, 또 다른 방법인 implements 키워드를 사용하는 방법을 사용하자.
++Thread 클래스에 start(), sleep(), yield() ..등 다양한 메서드가 있어 바로 사용할 수 있다는 장점이 있다.

2. implements Runnable 방법


public class Thread2 implements Runnable {

	public Thread2() {
		
	}
	//public Thread2(String name) { //생성자 이용
	//	Thread tt = new Thread(this,name);
	//	tt.start();
	//}
    @Override
	public void run() {
		for (int i = 1; i <= 10; i++) {
			System.out.println(i+": "+Thread.currentThread().getName());
			//currentThread() 현재 사용중인 쓰레드
			try {
				Thread.sleep(100); 
                //Unhandled exception type InterruptedException 예외처리
				//thread를 일정 시간동안 비활성화.
				
			} catch (Exception e) {
				System.out.println(e);
			}
		}
	}
    public static void main(String[] args) {
    	Ex38Thread2 my1 = new Ex38Thread2();
		Ex38Thread2 my2 = new Ex38Thread2();
		Thread t1 = new Thread(my1);
		t1.start();
		Thread t2 = new Thread(my2);
		t2.start();
        
    	//new Ex38Thread2("하나");
		//new Ex38Thread2("둘");
		
		System.out.println("종료");
	}

}

Runnable클래스를 implements하는 방법이다. Thread 클래스의 메서드를 사용하기 위해서는 인스턴스해야하는 약간의 단점이 있다.

💫자주 사용하는 Thread 메서드

스레드 상태를 제어하기 위해 다양한 메서드가 있는데, 크게 2가지로 나뉜다. (우선순위 메서드인 .setPriority()메서드는 어디에 둬야하나..)
우선, 스레드의 상태를 확인하는 메서드를 알아보자.

get.state()
 //현재 쓰레드 상태 확인
 System.out.println(thread.getState());
 
 //추가로 현재 사용중인 쓰레드 확인 (currentThread())
 System.out.println(Thread.currentThread().getName());

위와 같은 코드를 사용하면 현재 상황을 확인할 수 있다.

state()의 메서드를 열어보면 state0()

✨ 실행 대기 상태로 만드는 메서드
  • interrupt()
    일시 중지 상태인 스레드를 실행 대기 상태로 복귀시키는 메서드이다. 멈춘 스레드.interrupt()를 호출하여 정지 상태를 실행 대기 상태로 전환할 수 있다.
thread.interrupt();

이 메서드는 실행하는 강제하는 메서드이다.

  • yield()
    실행을 다른 스레드에게 양보하는 메서드이다.
    스케줄러에 의해 10초동안 할당받은 스레드가 있다면, 4초 동안 작업을 수행하다가 yield() 메서드를 호출하면, 남은 6초를 뒤의 스레드에게 양보하는 메서드이다.

메서드를 열어보면 설명이 나오는데,
Causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds, subject to the precision and accuracy of system timers and schedulers. The thread does not lose ownership of any monitors.
변역
현재 실행 중인 스레드를 절전 모드로 전환합니다 (일시적으로 중지)
execution) 시스템 타이머 및 스케줄러의 정밀도 및 정확도에 따라 지정된 밀리초 수에 대해. 스레드는 모니터의 소유권을 잃지 않습니다.


try{
	Ex37ThreadTest1 th1 =new Ex37ThreadTest1("one");
	th1.start();
    
	th1.yield();
} catch(Exception e){
	...
}
✨ 실행을 멈추는 메서드
  • sleep(long milliSecond)
try{
	Thread.sleep(1000); // 1000 = 1초
} catch (Exception e){
	System.out.print(e.getMessage)
}

sleep()메서를 사용하면 실행한 스레드의 상태는 일시 정지 상태가 된다. 인자로 전달한 시간이 지나거나 interrupt() 메서드를 호출한 경우 다시 실행 대기 상태로 복귀한다.
약간의 오차는 발생할 수 있다.

  • join()
try{
	Ex37ThreadTest1 th1 =new Ex37ThreadTest1("one");
	th1.start();
    
	th1.join();
} catch(Exception e){
	...
}

th1 스레드가 종료될 때까지 main 스레드 종료를 대기시키는 메서드이다.
join() 메서드를 열어보자.

(메서드를 잘 안 열어봐서 그런지 첫문장이 좀 웃겼다ㅋㅋㅋ)
현재 스레드가 죽기를 기다리는 메서드이다.

그 다음 문단이 약간 이해가 안가는데, "이 메스드의 호출은 호출과 정확히 동일한 방식으로 동작한다."는 문장이다..?

그리고 InterruptedException을 던지고 있다.
때문에 해당 메서드를 사용할때는 try-catch구문을 사용해야한다.

  • th1.setPriority(MAX_PRIORITY);
    스레드는 CPU 우선순위에 의해 동작되어 java에서 제어하기 힘들다. 스레드 스케줄러에게 우선 순위를 요청할 수 있다.
    .setPriority(...)이다.

    - 최대 우선 순위
    MAX_PRIORITY = 10
    - 최소 우선 순위
    MIN_PRIORITY = 1
    - 일반적 우선순위
    NORM_PRIORITY = 5
try{
	Ex37ThreadTest1 th1 =new Ex37ThreadTest1("one");
	th1.start();
    Ex37ThreadTest1 th2 =new Ex37ThreadTest1("two");
	th2.start();
    
	th2.setPriority(MAX_PRIORITY);
    
} catch(Exception e){
	...
}

물론 요청하는 것이기에 스레드 스케줄러에 무조건 적용이 되는 것은 아니다.

profile
읽히는 코드를 짜고싶습니다.

0개의 댓글