우리가 사용하는 프로그램은 하나의 프로세스이다. 프로그램을 실행하면 OS로부터 자원을 할당받아 프로세스가 된다.
프로세스는 프로그램을 수행하는데 필요한 데이터와 메모리 등의 자원과 쓰레드로 구성되어 있다. 프로세스의 자원을 이용해서 실제 작업을 수행하는 것이 바로 쓰레드이다.
Thread의 구현과 실행
자바에서는 쓰레드를 관리하기 위한 메서드와 변수들을 java.lang.Thread 클래스에서 제공한다.
쓰레드의 구현 방법으로는 Thread클래스 상속받는 방법과 Runnable 인터페이스를 구현하는 방법이 있다.
Thread 클래스를 상속
생성
public class HelloThread extends Thread{
@Override
public void run() { // 쓰레드가 수행할 작업을 작성
System.out.println("hello");
}
}
실행
public static void main(String[] args) {
HelloThread thread = new HelloThread();
thread.start();
}
thread 클래스를 상속한 경우 객체 인스턴스를 생성 한 뒤 strar()매서드를 호출.
Runnable 인터페이스 구현
생성
public class HelloRunnable implements Runnable {
@Override
public void run() { // 쓰레드가 수행할 작업을 작성
System.out.println("hello");
}
}
실행
public static void main(String[] args) {
HelloRunnable runnable = new HelloRunnable();
Thread thread = new Thread(runnable);
thread.start();
}
Runnable 인터페이스를 구현한 경우 객체 인스턴스를 thread 객체 생성자에 전달 한 뒤 start()메서드를 호출.
자바에서 쓰레드를 실행하기 위해서는 Runnable 인터페이스를 구현해야 한다.
runnable 인터페이스는 추상 메서드 run() 하나만 있는 함수형 인터페이스로 run() 메서드를 오버라이딩 하여 쓰레드를 실행할 수 있다.
start()메서드로 실행하는 이유는 멀티 쓰레드 프로그래밍을 할 수 있긴 때문이다.
쓰레드는 객체가 생성, 실행, 종료되기까지 다양한 상태를 가진다.
각 쓰레드는 Thread.state 상태로 정의되어있다.
Thread.state 내부에는 6개의 문자열 상수(NEW, RUNNABLE, TERMINATED, TIMED_WAITING, BLOCKED, WAITING)가 저장되어 있다.
쓰레드 상태 제어
실행중인 스레드의 상태를 변경하는 것
스레드에 대해 허용되는 우선 순위 값은 1에서 10사이이다.
Java에서 우선 순위를 가져오고 설정하는 메서드
우선 순위를 제일 높게 설정한다고 항상 먼저 실행됨을 보장 할 수는 없다.
메인 메소드가 실행이 되면 코드 한줄 한줄 순차적으로 시작하게 되고, return 을 만나거나, main 메서드의 끝이오면 종료하게 된다.
모든 자바 앱에서 메인 쓰레드는 main 메소드를 통해서 실행하게 된다.
public class App {
public static void main(String[] args) {
System.out.println("Main Thread 시작");
for (int i = 0; i < 10001; i++) {
System.out.println("hello");
}
return; // 종료
}
}
이런 메소드가 종료되면, 프로세스 자체도 종료된다.하지만 멀티 스레드를 구상 하였을때 메인 메소드가 끝이 나더라도 쓰레드 마지막이 끝나야 프로세스가 종료된다.
데몬 스레드
main 스레드를 보조하는 스레드. 메인스레드가 종료되면 데몬 스레드도 강제적으로 종료.
싱글 스레드, 즉 메인 스레드 한개로 구성된 앱에서는 문제가 되지 않겠지만, 만약 멀티 스레드 상황에서 객체를 생성한다면, 여러개의 스레드가 객체를 건들 수 있다.
이런 상황에서 지금 사용중인 객체를 다른 스레드가 변경할 수 없도록 하려면 스레드가 작업이 끝날 때 까지 객체에 잠긍을 걸어서 다른 스레드가 사용할 수 없도록 한다.
임계영역
멀티 스레드에서 단 하나의 스레드만 실행할 수 있는 코드 영역을 임계 영역이라고 한다.
자바는 이런 임계 영역을 지정하기 위해서 동기화 메소드와 동기화 블록을 제공하게 된다. 동기화 메소드를 만드는 방법은 메소드 선언에 synchronized 키워드를 붙이면 된다. (동기화 블록을 선언하는 것도 좋은 방법이다.)
둘 이상의 스레드가 lock을 획득하기 위해 기다리는데, 이 lock을 잡고 있는 스레드도 똑같이 다른 lock을 기다리며 서로 블록 상태에 놓이는 것을 말한다. 데드락은 다수의 쓰레드가 같은 lock을, 동시에, 다른 명령에 의해 획득하려 할 때 발생한다.
EX.
public class TreeNode {
TreeNode parent = null;
List children = new ArrayList();
public synchronized void addChild(TreeNode child){
if(!this.children.contains(child)) {
this.children.add(child);
child.setParentOnly(this);
}
}
public synchronized void addChildOnly(TreeNode child){
if(!this.children.contains(child){
this.children.add(child);
}
}
public synchronized void setParent(TreeNode parent){
this.parent = parent;
parent.addChildOnly(this);
}
public synchronized void setParentOnly(TreeNode parent){
this.parent = parent;
}
}
TreeNode 클래스의 예제를 이용해 알아보자.
스레드1이 parent.addchild메소드를, 스레드2는 child.setparent메소드를 각각 동시에, 같은 parent와 child 인스턴스에 호출한다면, 데드락이 발생할 수 있다.
https://wisdom-and-record.tistory.com/48
https://connie.tistory.com/12
https://handr95.tistory.com/37