본문 바로가기
Challenge

10주차 과제: 멀티쓰레드 프로그래밍

by Daisy :) 2021. 1. 23.
반응형

process와 thread

프로그램을 실행하면 OS로부터 실행에 필요한 자원(메모리)을 할당받아 프로세스가 됩니다.

프로세스는 프로그램을 수행하는데 필요한 데이터와 메모리등의 자원 그리고 쓰레드로 구성되어 있으며, 프로세스의 자원을 이용해서 실제로 작업을 수행하는 것이 쓰레드입니다.

 

싱글쓰레드는 한번에 한가지 작업만 가능하지만, 멀티쓰레드는 한번에 여러가지 작업이 가능합니다.

 

● 멀티쓰레딩의 장점
 - CPU의 사용률을 향상시킨다.
 - 자원을 보다 효율적으로 사용할 수 있다.
 - 사용자에 대한 응답성이 향상된다.
 - 작업이 분리되어 코드가 간결해진다.

※ 멀티쓰레드 프로세스는 여러 쓰레드가 같은 프로세스 내에서 자원을 공유하면서 작업을 하기 때문에 발생할 수 있는 동기화(synchronization), 교착상태(deadlock)와 같은 문제들을 고려해서 신중히 프로그래밍 해야합니다.

자바의 모든 쓰레드는 java.lang.thread 클래스 객체에 의해 생성되고 제어됩니다. 

독립적인 응용프로그램이 실행될 때, main() 메서드를 실행하기 위한 하나의 사용자 쓰레드가 자동으로 만들어지는데 이 쓰레드를 주쓰레즈(main thread)라고 부릅니다. 

Thread 클래스와 Runnable 인터페이스

자바에서 쓰레드를 구현하는 방법으로는 두가지가 있습니다.

 

- java.lang.Runnable 인터페이스를 구현하기

- java.lang.Thread 클래스 상속받기 

 

Runnable 인터페이스 구현방법

class MyThread interface Runnable{
	public void run() {/*작업내용*/} //Runnable 인터페이스의 run()을 구현
}
public class RunnableThreadExample implements Runnable{
	
	public int count = 0;
	
	public void run() {
		System.out.println("RunnableThread starting.");
		try {
			while(count<5) {
				Thread.sleep(500);
				count++;
			}
		}catch(InterruptedException exc) {
			System.out.println("RunnableThread interrupted.");
		}
		System.out.println("RunnableThread terminating.");
	}


	public static void main(String[] args) {
		
		RunnableThreadExample instance = new RunnableThreadExample();
		Thread thread = new Thread(instance);
		thread.start();
		
		while (instance.count != 5) {
			try {
				Thread.sleep(250);
			}catch(InterruptedException exc) {
				exc.printStackTrace();
			}
		}
	}
}

Thread 클래스 상속

class MyThread extends Thread{
	public void run() { /* 작업내용 */} //Thread 클래스의 run()을 오버라이딩
}

Thread클래스를 상속받아서 쓰레드를 만들때는 거의 항상 run()메서드를 오버라이드 해야하며, 하위 클래스의 생성자는 상위클래스의 생성자를 명시적으로 호출해야합니다.

 

class TheadExample {
	public static void main(String[] args) {
		ThreadEx1_1 t1 = new ThreadEx1_1(); //Thread 자손클래스의 인스턴스 생성 
		
		t1.start();
	}
}

class ThreadEx1_1 extends Thread{
	public void run() {
		for(int i = 0; i<5;i++) {
			System.out.println(getName()); //조상인 Thread의 getName()을 호출
		}
	}
}

 

자바는 다중상속을 지원하지 않기때문에 Thread클래스를 상속하게 되면 하위 클래스는 다른 클래스를 상속할 수가 없습니다. 하지만 Runnable인터페이스를 구현하는 클래스는 다른 클래스를 상속할 수 있기때문에 Thread를 상속받는 것 보다 Runnable 인터페이스를 구현하는 것이 더 선호됩니다.

 

쓰레드의 상태

상태 설명
NEW 쓰레드가 생성되고 아직 start()가 호출되지 않은 상태
RUNNABLE 실행 중 또는 실행가능한 상태
BLOCKED 동기화 블럭에 의해서 일시정지된 상태(lock이 풀릴 때까지 기다리는 상태)
WAITING,
TIMED_WAITING
쓰레드의 작업이 종료되지는 않았지만, 실행가능하지 않은 일시정지 상태. 
TIMED_WAITING은 일시정지시간이 지정된 경우를 의미
TERMINATED 쓰레드의 작업이 종료된 상태

 

쓰레드의 우선순위

우선순위는 Thread 클래스의 setPriority() 메소드로 간단히 부여할 수 있습니다.

void setPriority(int newPriority) //쓰레드의 우선순위를 지정한 값으로 변경한다.
int getPriority() //쓰레드의 우선순위를 반환한다.
public class ThreadExample2 {

	public static void main(String[] args) {
		ThreadEx1 th1 = new ThreadEx1();
		ThreadEx2 th2 = new ThreadEx2();
		
		th2.setPriority(7);
		
		System.out.println("Priority of th1(-) : "+ th1.getPriority());
		System.out.println("Priority of th2(-) : "+ th2.getPriority());
		
		th1.start();
		th2.start();
	}
}

class ThreadEx1 extends Thread{
	public void run() {
		for(int i=0; i<10; i++) {
			System.out.println("-");
			for(int x=0; x<100; x++);
		}
	}
}
class ThreadEx2 extends Thread{
	public void run() {
		for(int i=0; i<10; i++) {
			System.out.println("|");
			for(int x=0; x<100; x++);
		}
	}
}

 

Main 쓰레드

main메서드의 작업을 수행하는 것도 메인쓰레드입니다.

이러한 Main Thread 흐름 안에서 싱글 스레드가 아닌 멀티 스레드 어플리케이션은 필요에 따라 작업 쓰레드를 만들어 병렬로 코드를 실행할 수 있습니다. 싱글 스레드 같은 경우 메인 스레드가 종료되면 프로세스도 종료되지만, 멀티 스레드는 메인 스레드가 종료되더라도 실행 중인 스레드가 하나라도 있다면 프로세스는 종료되지 않습니다.

 

동기화(Synchronization)

어떤 프로세스안에서 생성된 스레드들은 같은 메모리 공간을 공유하면서 장단점이 발생됩니다. 

스레드가 서로 데이터를 공유할 수 있다는 장점은 있지만, 두 쓰레드가 같은 자원을 동시에 변경하는 경우에는 문제가 됩니다.

자바는 공유 자원에 대한 접근을 제어하기 위한 동기화 방법을 제공합니다. 

쓰레드의 동기화 - 한 쓰레드가 진행중인 작업을 다른 쓰레드가 간섭하지 못하게 막는 것.
synchronized를 이용한 동기화

1. 메서드 전체를 임계영역으로 지정
public synchronized void calcSum(){

}

2. 특정한 영역을 임계영역으로 지정
synchronized(객체의 참조변수){
	
}

1. 메서드 앞에 synchrocized를 붙이는 것인데, synchrocized를 붙이면 메서드 전체가 임계영역으로 설정됩니다. 

   쓰레드는 synchrocized메서드가 호출된 시점부터 해당 메서드가 포함된 객체의 lock을 얻어 작업을 수행하다가 메서드가 종료되면

   lock을 반환합니다

2. 메서드 내의 코드 일부를 블럭{}으로 감싸고 블럭앞에 synchrocized를 붙이는 것인데, 이때 참조변수는 락을 걸고자 하는 객체를

   참조하는것이어야합니다. 

   이 블럭을 synchrocized블럭이라고 부르며, 이 블럭의 영역 안으로 들어가면서부터 쓰레드는 지정된 객체의 lock을 얻게 되고,

   이 블럭을 벗어나면 lock을 반납합니다.

 

데드락(deadlock, 교착상태)

첫번째 스레드는 두번쨰 스레드가 들고 있는 객체의 락이 풀리기를 기다리고 있고, 두번쨰 스레드 역시 첫번쨰 쓰레드가 들고 있는 객체의 락이 풀리기를 기다리는 상황을 말합니다. 모든 쓰레드가 락이 풀리기를 기다리고 있기 떄문에 무한 대기상태에 빠지게 되는데 이런 쓰레드를 교착상태에 빠졌다고 합니다. 

 

교착상태의 조건 

1) 상호 배제(Mutual Exclusion) : 프로세스들이 자원을 배타적으로 점유하고 있어, 다른 프로세스들이 자원을 사용할 수 없게 만듦 (자원의 배타적인 제어권)

 

2) 점유와 대기(Hold & Wait) : 최소한 하나의 자원을 점유하고 있는 프로세스가 존재해야하며, 이 프로세스는 다른 프로세스에 할당된 자원을 추가로 점유하기 위해 대기함

 

3) 비선점(Non-preemption) : 비선점 자원들은 그들을 점유하는 프로세스로부터 벗어나지 못한다.(도중에 해지될 수 없음) 점유한 프로세스만이 자원을 해재할 수 있다.

 

4) 환형 대기(Circular Wait) : 프로세스와 자원들이 원형을 이루며, 각 프로세스가 자신에게 할당된 자원을 가지면서 상대방 프로세스의 자원을 상호 요청하는 경우

 

교착상태를 방지하기 위해선 이 조건들 중 하나를 제어하면 되지만 까다롭습니다.

대부분 교착상태 방지 알고리즘은 대시강태의 사이클이 발생하는 일을 막는데 초점이 맞춰져있습니다.



 

 

<Reference>

자바의 정석 저자 : 남궁 성 

코딩인터뷰 완전분석 게일 라크만 맥도웰

honbabzone.com/java/java-thread/

 

반응형

'Challenge' 카테고리의 다른 글

12주차 과제: 애노테이션 #12  (0) 2021.02.12
11주차 과제: Enum  (0) 2021.01.29
9주차 과제: 예외 처리  (0) 2021.01.15
8주자 과제: 인터페이스  (0) 2021.01.08
7주차 과제: 패키지  (0) 2021.01.02

댓글