본문 바로가기
Java

Thread, multi-threading (스레드)

by 개발자공부 2024. 5. 1.

목차

Thread

Thread 추가로 만들어서 사용하기

Thread실행시 스레드 상태(Thread Status)


 

Thread

  컴퓨터에서 다수의 사용자들을 동시에 처리할 수 있는 프로그램이 각각의 사용과 관련하여 가지고 있는 정보들.

 

  스레드는 프로세스 내에서 실제로 작업을 수행하는 단위이다. 모든 자바 프로그램은 메인 스레드에서 시작되며, 사용자는 추가적인 스레드를 생성할 수 있다. 각 스레드는 독립적으로 실행되며, 자신만의 호출 스택을 가진다. (각 스레드의 스택 영역은 서로 격리되어 있다.) 프로세스 내의 모든 스레드는 같은 Heap 메모리 공간과 다른 자원을 공유할 수 있다.

public class MyThread extends Thread{
	// 메인 함수 - 메인 스레드(메인 작업자)
    // 기본적으로 메인 작업자를 하나 가지고 있다. = 메인 스레드
    // 필요하면 스레드를 더 추가할 수도 있다.
	public static void main(String[] args){
    	System.out.println("스레드");
    }
}

 

■ Process 

  저장되어 있는 프로그램이 실행되면 OS로부터 메모리를 할당받아 프로세스 상태가 된다.

하나의 프로세스는 하나 이상의 thread를 가지게 되고, 실제 작업을 수행하는 단위는 thread이다.

ctrl + alt + del로 작업관리자 들어가면 각 프로그램이 할당받은 메모리 용량을 확인할 수 있다.

 

처리하는 작업자(Thread)가 하나인데 작업해야 할 것(Stack)은 두 개라면 충돌이 일어날 수 있다. 그래서 작업자를 하나 더 생성해주어서 동시 진행하는 것이 좋다.

 

 

 

 


Thread 추가로 만들어서 사용하기

1. 첫 번째 방법 - Thread 클래스를 상속하여 만들 수 있다.

더보기
	// 스레드 추가하는 방법
    // 첫 번째 - 상속을 활용한다. Thread -> Runnable -> run()
    
public class MyThread extends Thread{

    // 정의한 클래스에서 run() 메서드를 재정의할 수 있다.
    // 스레드를 사용할 때 알아야 하는 필수 개념이며 이해가 되지 않아도 일단 외워야 한다.
    // 스레드가 가지고 있는 메서드 start()를 호출시키면 약속으로
    // run() 메서드가 호출되어 동작하게 만들어져 있다.
    
    @Override
    public void run(){
    
    // 동작시키고자 하는 일을 정의해서 코드를 설계할 수 있다.
    // 반복문 200번 수행하는 코드
    	for(int i = 0; i < 200; i++){
        	System.out.println(i);
            
            try{
            	Thread.sleep(500);
            } catch(InterruptedException e){
            	e.printStackTrace();
            }
            
        } // end of for
        
    } // end of run()
    
} // end of class
public class MyThreadMainTest{

	public static void main(String[] args){
    	System.out.println(Thread.currentThread());
        
        // 필요하면 추가 작업자를 사용할 수 있다.
        MyThread th1 = new MyThread();
        th1.start(); 
        // start()메소드는 스레드 안에 있다.
        // Thread 안에 있는 start()호출 --> run()메소드가 동작되도록 약속되어 있다.
        
        // 필요하면 추가 작업자를 더 만들 수 있다.
        MyThread th2 = new MyThread();
        th2.start();
    } // end of main

} // end of class
public class Worker extends Thread{

	private String name;
    
    public Worker(String name){
    	this.name = name;
    }
    
    // 약속된 부분인 run() 메서드를 정의하면 된다.
    @Override
    public void run(){
    
    	for(int i = 0; i < 50; i++){
        	System.out.println("worker : " + name + " : " + i);
            
            try{
            	Thread.sleep(200);
            } catch(InterruptedException e){
            	e.printStackTrace();
            }
            
        } // end of for
        
    } // end of run()

} // end of class
public class WorkerMainTest{
	
    // 메인 작업자 main thread
    public static void main(String[] args){
    	System.out.println("--- start main thread ---");
    	System.out.println(Thread.currentThread());
        
	// 작업자 하나 더 만들어 내기
    Worker worker1 = new Worker("워커1"); // <-- 생성은 메인 스레드가 했다.
    worker1.start(); // <-- 메인 스레드가 명령을 내렸고, worker1은 위임받은 일을 시작한다.
    
    System.out.println("--- end main thread ---");
    
    } // end of main
    
} // end of class

 

2. 두 번째 방법 - Runnable 인터페이스를 구현하여 만들 수 있다.

더보기
public class MyThread2 implements Runnable{

	// Thread 클래스에서 start 메소드가 호출되면 
    // 동작하는 부분이라고 약속되어 있다.
	@Override
    public void run(){
    	int i = 0;
        while(i < 10){
        	System.out.println("i : " + i);
            i++;
            
            try{
            	Thread.sleep(1000);
            } catch(InterruptedException e){
            	e.printStackTrace();
            }
        } // end of while
    } // end of run()

} // end of class
public class MyThread2MainTest{

	public static void main(String[] args){
    	System.out.println("main start");
        // MyThread2를 메모리에 올리고 그 안에 정의된 run()메서드를 호출하고 싶다.
        // 힌트 --> Thread 문서에서 생성자 확인하기.
        MyThread2 myThread2 = new MyThread2();
		// 여기서 만약 myThread2.run();을 호출하면 이건 일반 메서드를 호출하는 것이다.
        
        // 새로운 작업자를 생성해서 위임 시킬때는 Thread 안에 있는 start()메서드를 호출해야 한다.
        // 결국 다른 작업자에게 일을 위임하려면 >>스레드<<에 start()를 호출해야 한다.
        Thread thread1 = new Thread(myThread2);
        thread1.start();
        
        System.out.println("--- end main thread ---");
        
    } // end of main
    
} //  end of class

 


 

Thread 실행시 스레드 상태 (Thread Status)

 


 

멀티 스레딩 Multi-threading

  자바에서 멀티 스레딩은 프로그램의 여러 부분이 동시에 실행되도록 하는 기술이다. 이를 통해 자원의 효율적 사용과 응용 프로그램의 반응성 향상을 달성할 수 있다. 멀티 스레딩은 하나의 프로세스 내에서 여러 개의 스레드를 생성하여 각 스레드가 작업을 수행하도록 함으로써 병렬 처리를 가능하게 한다.

 

정리

☞ 여러 스레드가 동시에 수행되는 프로그래밍, 여러 작업이 동시에 실행되는 효과

☞ 스레드는 각각 자신만의 작업 공간을 가진다. = call Stack

☞ 각 스레드 사이에서 공유하는 자원이 있을 수 있다. (자바에서는 static instance)

☞ 여러 스레드가 자원을 공유하여 작업이 수행되는 경우 서로 자원을 차지하려는 race condition이 발생할 수 있다.

☞ 이렇게 여러 스레드가 공유하는 자원 중 경쟁이 발생하는 부분을 critical section이라고 한다.

☞ critical section에 대한 동기화(일종의 순차적 수행)를 구현하지 않으면 오류가 발생할 수 있다.

 

멀티 스레딩 장점

자원을 효율적으로 사용 : CPU 사용률을 향상시키고 자원을 효율적으로 사용할 수 있다.

응용 프로그램의 반응성 향상 : 긴 작업을 처리하는 동안 사용자 인터페이스가 멈추지 않고 반응할 수 있다.

병렬 작업 : 여러 하드웨어 코어를 활용하여 작업을 병렬로 처리할 수 있어 실행 시간을 단축시킨다.

 

주의사항

  멀티 스레딩 환경에서는 여러 스레드가 동일한 자원에 동시에 접근할 때 발생할 수 있는 문제들(예:경쟁상태, 교착상태)을 고려해야 한다. 이러한 문제를 해결하기 위해 자바는 동기화(synchronization) 메커니즘을 제공한다.

 

멀티 스레드 프로그래밍에서 동기화 (synchronization)

☞ 스레드 두 개가 같은 객체에 동시 접근하면 오류 발생

☞ 동기화는 임계영역에 접근한 경우 공유자원을 lock하여 다른 스레드가 접근 못하게 제어한다.

☞ 자바에서는 동기화 메서드(synchronization methods)나 동기화 블럭(synchronization block)을 사용한다.

 

해결방안

동기화 메서드

객체 메소드에 synchronization 키워드 사용 → 현재 이 메소드가 속해있는 객체에 lock을 건다.

동기화 블럭

현재 객체 또는 다른 객체를 lock로 만든다.

 

시나리오 코드

각 스레드가 공유하는 자원 만들어 보기 (sharedResource 상황을 구현해보자)

두 사람이 같은 계좌를 공유하고 있다. 계좌 잔액은 10만원이며, 한 사람은 1만원을 입금했고 한 사람은 5천원을 출금했다.

멀티 스레드 동기화를 사용하여 계좌 잔액 10만 5천원을 출력하시오.

'Java' 카테고리의 다른 글

바이트 기반 - 파일 입출력/Copy  (0) 2024.05.20
I/O, 표준 입출력  (0) 2024.05.20
Exception(예외처리)  (0) 2024.04.29
String, StringBuffer 클래스  (0) 2024.04.29
Object 클래스  (0) 2024.04.29