wallnut's 2020. 6. 5. 23:45

프로세스와 스레드

프로세스란 간단히 말해 실행 중인 프로그램입니다 프로그램을 실행하려면 OS로부터 실행에 필요한 자원을 할당받아 프로세스가 됩니다

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

그래서 모든 프로세스에는 최소한 하나 이상의 스레드가 존재하며 둘 이상의 스레드를 가진 프로세스를 멀티스레드 프로세스라고 합니다

하나의 프로 스세가 가짓수 있는 스레드의 개수는 제한되어있지 않으나 스레드가 작업을 수행하는데 개별적인 메모리 공안을 필요로 하기 때문에 프로세스의 메모리 한계에 따라 생성할 수 있는 스레드의 수가 결정됩니다 실제로는 프로세스의 메모리 한계에 다다를 정도로 많은 스레드를 생성하는 일은 없습니다.

 

스레드를 구현하는 방법은 Thread클래스를 상속받는 방법과 Runnable 인터페이스를 구현하는 방법 모두 두 가지가 있습니다 어느 쪽을 선택해도 별 차이는 없지만 Thread클래스를 상속받으면 다른 클래스를 상속받을 수 없기 때문에 Runnable인터페이스를 구현하는 방법이 일반적입니다

1.Thread클래스를 상속
class MyThread extends Thread{
	public void run() {
	}
}
2.Runnable인터페이스를 구현
class MyThread01 implements Runnable{
	public void run(){
		
	}
}

Runnable 인터페이스는 오로지 run()만 정의되어있는 간단한 인터페이스입니다 Runnable인터페이스를 구현하기 위해서 해야 할 일은 추상적인 메서드인 run()의 몸통{}을 만들어 주는 것뿐입니다.

하지만 스레드를 구현한다는 것은 위의 두 방법 중에 어떤 것을 선택하든 그저 스레드를 통해 작업하고자 하는 내용으로 run()의 몸통{}을 채우는 것뿐입니다.

그리고 Thread클래스를 상속받은 경우와 Runnable인터페이스를 구현한 경우의 인스턴스 생성 방법은 다릅니다

ThreadEx1_1 t1 = new ThreadEx1_1();//Thread자손클래스의 인스턴스 생성
Runnable r = new ThreadEx1_2();//Runnable을 구현한 클래스의 인스턴스를 생성
Thread t2 = new Thread(r);//생성자 Thread(Runnable target)
Thread t2 = new Thread(new ThreadEx1_2())//위의 두줄을 한줄로 간단히

Runnable인터페이스를 구현한 경우 Runnable인터페이스를 구현한 클래스의 인스턴스를 생성한 다음 이 인스턴스를 Thread 클래스의 생성자의 매개변수로 제공해야 합니다 그리고 Thread 클래스를 상속받으면 자손 클래스에서 조상인 Thread 클래스의 메서드를 직접 호출할 수 있지만 Runnable을 구현하려면 Thread클래스의 static메서드인 current Thread()를 호출하여 스레드에 대한 참조를 얻어와야만 가능합니다.

static Thread  currentThread()//현재 실행중인 쓰레드의 참조를 반환합니다
String getName()//쓰레드의 이름을 반환합니다

추가로 스레드의 이름은 생성자나 메서드를 통해 지정 또는 변경할 수 있습니다.

start()와 run

스레드는 생성했다고  자동으로 실행되지 않습니다 start()를 호출해야만 스레드가 실행됩니다

main메서드에서 run()을 호출하는 것은 생성된 스레드를 실행시키는 것이 아니라 단순히 클래스에 선언된 메서드를 호출하는 것 일뿐입니다 반면에 start는 새로운 스레드가 작업을 실행하는데 필요한 호출 스택을 생성한 다음에 run()을 호출해서 생성된 호출 스택에 run() 첫 번째로 올라가게 합니다

모든 스레드는 독립적인 작업을 수행하기 위해 자신만의 호출 스택을 필요로 하기 때문에 새로운 스레드를 생성하고 실행시킬떄마다 새로운 호출 스택이 생성되고 스레드가 종료되면 작업에 사용된 호출 스택은 소멸됩니다

그리고 스레드가 둘이 상일 때는 호출 스택의 최상위에 있는 메서드 일지라도 대기상태에 있을 수 있습니다 스케줄러는 대기 중인 스레드들의 우선순위를 고려하여 실행순서와 시간을 결정하고 각쓰레드들은 작성된 스케줄에따라 자신의 순서가 되면 지정된 시간동안 작업을 수행합니다 이때 주어진 시간동안 작업을 마치지 못한 스레드는 다시 자신의 차례가 돌아올 때까지 대기상태로 있게 되며 작업을 마친 쓰레드 즉 run()의 수행이 종료된 쓰레드는 호출 스택이 모두 비워지면서 스레드가 사용하던 호출 스택은 사라집니다.

쓰레드의 동기화

쓰레드를 하나만 사용하는 싱글 쓰레드 프로세스의 경우 프로세스내에 단 하나의 쓰레드만 작업하기때문에 프로세스의 자원을 가지고 작업하는데 별문제가 없지만 멀티 쓰르드 프로스세의 경우 여러 쓰레드가 같은 프로세스 내의 자원을 가지고 작업하는데 별문제가 없지만 2개이상의 쓰레드를 기지는 멀티쓰레드 프로세스의 경우 여러쓰레드가 같은 프로세스 내의 자원을 공유해서 작업을 하기떄문에 서로의 작업에 영향을 주게됩니다 만일 쓰레드 A가 작업하던 도중에 다른 쓰레드 B에게 제어권이 넘어 갔을떼쓰레드A가 작업하던 공유데이터를 쓰레드B가임의로 변경하였다면 다시 쓰레드 A가 제어권을 받아서 나머지 작업을 마쳤을떄 원래 의도했던 것과 다른 결과를볼수있습니다 이는 마치 한방의 여러 사람이 방안의 컴퓨터를 함께 나워 쓰는 상화과 같아서 한 사람이 컴퓨터로 문서 작업 도중에 잠시 자리를 비웠을 떄 다른 사람이 컴퓨터를 만져서 앞 사람이 작업하던 문서가 지워진다던가하는 일이 생길수 있습니다

이럴 때는 문서작업이 끝날떄 까지 컴퓨터에 비밀번호를 걸어서 다른사람이 사용할수 없도록 해야합니다.

synchronized를 이용한 동기화

자바에서는 키워드 synchronized를 통해 해당 작업과 관련된 공유 데이터에lock을 걸어 서 먼저 작업중이던 쓰레드가 작업을 완전히 마칠떄까지는 다른 쓰레드에게 제어권이 넘어가더라도 데이터가 변경되지 않도록 보호함으로써 쓰레드의 동기화를 가능하게합니다

wait()과 naotify()

쓰레드를 동기화 할때 동기화의 효율을 높이기 위해 wait()과 naotify()를 함께 사용할 수있습니다 한 쓰레드가 객체에 lock를 걸고 어떤 조건이 만족될 때까지 기다려야하는 경우 이 쓰레드를  그대로 놔두면 이객체를 사용하려는 다른 쓰레드들을 lock이 풀릴떄까지 같이 기다려야하는 상황이 발생합니다

이러한 비효율을 개선하기위해서 wait()과 naotify()사용합니다 한쓰레드가 객체에lock을 걸고 오래기다리는 대신 wait()을 호출해서 다른 쓰레드에게 제어권을 넘겨주고 대기상태로 기다리다가 다른 쓰레드에 의해서 notify()가 호출되면 다시 실행상태가 되도록 하는 것입니다 이는 마치 식당에 자리가 날떄까지 서서 계속 기다리기 보다 식당의 대시길에서 기다리고 있다가 자리가 나면 통보를 받는것이 더 효율적인 것과 유사하다고 할수 있습니다.

 

wait()과 naotify()는 Thread클래스가 아닌 Object클래스에 정의된 메서드이므로 모든 객체에서 호출이 가능합니다 그리고 동기화 블록 내에서만 사용할 수 있으며 쓰레드가 wait()을 호출하면 그떄까지 자신이 객체에 걸어 놓았던 모든 lock을 풀고 wait()이 호출된 객체의 대기실에서 기다리게 됩니다 그러다가 다른쓰르ㅔ드에 의해 그 객체에 대해 notify()를 호출하면 객체의 대기실에서 벗어나서 다시 실행 대기상태 즉 객체의 waiting pool을 벗어나 실행대기 열에서 자신이 실행될 차를를 기다리는 상태가됩니다

notify()는 객체의 waiting pool에 있는 쓰레드중의 하나만꺠우고 notifyALL()은 모든 쓰레드를 꺠웁니다 어차피 한벙에 하나의 쓰레드만 객체를 사용할 수있기 떄문에 notify()를 사용하나 notifyAll()을 사용하다 별차이는 없습니다 그러다 notify()에 의해 어떤 쓰레드가 꺠워지게될지 알수가 없어 우선순위가 높은 특정쓰레드가 오랫동안 객체의 waiting pool에 머물수 있기 떄문에 다시 객체의 waiting pool에 들어가더라도 notifyAll()을 이용해서 모든 쓰레드를 깨워 놓고 쓰레드 JVM스케줄링에 의해서 처리되도록 하는것이 안전합니다