[ 자바의 스레드 ]
자바의 모든 스레드는 java.lang.Thread 클래스 객체에 의해 생성되고 제어됩니다. 자바에서 스레드를 구현하는 방법은 Runnable 인터페이스를 구현하는 법과 Thread 클래스를 상속받는 법으로 2가지가 있습니다.
⭐ 주 스레드(main thread) : 독립적인 응용 프로그램이 실행될 때, main() 메서드를 실행하기 위한 하나의 사용자 스레드
👀 Runnable 인터페이스를 구현하는 방법
✔️ 구조
public interface Runnable{
void run();
}
✔️ 과정
- Runnable 인터페이스를 구현하는 클래스를 만듭니다.
→ 이 클래스의 객체는 Runnable의 객체가 됩니다. - Thread 타입의 객체를 만들 때, Thread의 생성자에 Runnable 객체를 인자로 넘깁니다.
→ 이 Thread 객체는 이제 run() 메서드를 구현하는 Runnable객체를 소유하게 됩니다. - 이전 단계에서 생성한 Thread 객체의 start() 메서드를 호출합니다.
👀 Thread 클래스 상속
✔️ 구조
public class ThreadExample extends Thread{
public void run(){
Thread.sleep(500);
System.out.println("Thread!");
}
}
public class Example{
public static void main(String args[]){
ThreadExample instance = new ThreadExample();
instance.start();
}
}
✔️ 과정
- Thread 클래스를 상속받아서 run() 메서드를 오버라이드하여 구성합니다.
- 인스턴스 자체에서 start()를 직접 호출합니다.
👀 Runnable 인터페이스 vs Thread 상속 구현
자바는 다중 상속을 지원하지 않기 때문에 Thread 클래스를 상속하게 되면 하위 클래스는 다른 클래스를 상속할 수 없습니다. 그리고 Thread 클래스의 모든 것을 상속받는 것이 너무 부담되기 때문에 Runnable 인터페이스를 구현하는 것을 선호합니다.
[ Synchronized와 Lock ]
프로세스 안에서 생성된 스레드들은 같은 메모리 공간을 공유합니다. 스레드들이 같은 자원을 동시에 변경하는 경우 문제가 생기므로 자바는 공유 자원에 대한 접근을 제어하기 위한 동기화(synchronization) 방법을 제공합니다.
👀 동기화된 메서드
✔️ synchronized 키워드
공유 자원에 대한 접근을 제어 및 여러 스레드가 같은 객체를 동시에 실행하는 것도 방지합니다. 메서드 및 특정한 코드 블록에 적용 가능합니다.
public class Test extends Thread{
private String name;
private TestObject testobj;
public TestObject(TestObject obj, String n){
name = n;
testobj = obj;
}
public void run(){
testobj.test(name);
}
}
public class TestObject{
public synchronized void test(String name){ ... }
}
/* 가능 */
TestObject obj1 = new TestObject();
TestObject obj2 = new TestObject();
Test thread1 = new Test(obj1, "1");
Test thread2 = new Test(obj2, "2");
thread1.start();
thread2.start();
/* 불가능 */
TestObject obj = new TestObject();
Test thread1 = new Test(obj, "1");
Test thread2 = new Test(obj, "2");
thread1.start();
thread2.start();
⭐ 정적 메서드는 클래스 락(class lock)에 의해 동기화됩니다. 같은 클래스에 있는 동기화된 정적 메서드는 두 스레드에서 동시에 실행될 수 없습니다.
public class Test extends Thread{
...
public void run(){
if (name.equals("1")) testobj.test(name);
else testobj.test1(name);
}
}
public class TestObject{
public static synchronized void test(String name){ ... }
public static synchronized void test1(String name){ ... }
}
/* 동시에는 불가능 test 끝나고 test1 시작함*/
TestObject obj = new TestObject();
Test thread1 = new Test(obj, "1");
Test thread2 = new Test(obj, "2");
thread1.start();
thread2.start();
👀 동기화된 블록
메서드를 동기화하는 것과 아주 비슷하게 동작합니다.
public class Test extends Thread{
...
public void run(){
testobj.test(name);
}
}
public class TestObject{
public void test(String name){
synchronized(this){ ... }
}
}
TestObject obj = new TestObject();
Test thread1 = new Test(obj, "1");
thread1.start();
👀 Lock
좀 더 세밀하게 동기화를 제어하고 싶을 때 사용합니다. 공유 자원에 붙이면 해당 자원에 대한 접근을 동기화할 수 있습니다. 스레드가 해당 자원에 접근하려면 락을 얻어야 하므로 해당 자원은 한 번에 한 스레드만 사용할 수 있습니다.
예제) ATM 입금 및 출금
public class ATM{
private Lock lock;
...
public class ATM(){
lock = new ReentrantLock();
}
public int withdraw(int value){
lock.lock();
...
lock.unlock();
...
}
public int deposit(int value){
lock.lock();
...
lock.unlock();
...
}
}
[ 교착상태와 교착상태 방지 ]
👀 교착상태
: 첫 번째 스레드는 두 번째 스레드가 들고 있는 객체의 락이 풀리기를 기다리고 있고, 두 번째 스레드 역시 첫 번째 스레드가 들고 있는 객체의 락이 풀리기를 기다리는 상황입니다.
👀 교착상태에 빠지는 4가지 조건
- 상호 배제 : 한 번에 한 프로세스만 공유 자원을 사용합니다. 공유 자원에 대한 접근 권한이 제한됩니다. 자원의 양이 제한되어 있어도 교착상태는 발생할 수 있습니다.
- 들고 기다리기 : 공유 자원에 대한 접근 권한을 양보하지 않은 상태에서 다른 자원에 대한 접근 권한을 요구합니다.
- 선취 불가능 : 한 프로세스가 다른 프로세스의 자원 접근 권한을 강제로 취소 불가능합니다.
- 대기 상태의 사이클 : 두 개 이상의 프로세스가 자원 접근을 기다리는데 그 관계에 사이클이 존재합니다.
👀 교착상태 방지
위의 조건들 가운데 하나를 제거하면 됩니다.
'프로그램 개발 > 미분류' 카테고리의 다른 글
[코딩 인터뷰]지식 기반 문제 - 스레드와 락 문제 (0) | 2023.01.13 |
---|---|
[코딩 인터뷰]지식 기반 문제 - 데이터베이스 문제 (0) | 2023.01.12 |
[코딩 인터뷰]지식 기반 문제 - 데이터베이스 (0) | 2023.01.12 |
[코딩 인터뷰]지식 기반 문제 - Java 문제 (0) | 2023.01.11 |
[코딩 인터뷰]지식 기반 문제 - Java (0) | 2023.01.11 |