까마귀코딩.log
백기선 자바스터디 10주차 본문

<< 총 15주차 까지 진행되며 주어지는 키워드를 가지고 블로그에 정리하며 공부하는 스터디 입니다 ! >>
목표
젤리의 다중 스레드 프로그래밍에 대해 알아보십시오.
학습할 것 (필수)
- Thread 클래스와 Runnable 인터페이스
- 쓰레드의 상태
- 쓰레드의 우선 순위
- 메인 쓰레드
- 부정
- 데드락
Thread 클래스와 Runnable 인터페이스
쓰레드란 뭘까 ?
프로그램 == 프로세스
프로그램을 실행하면 ? => 프로세스가 된다.
=> OS로부터 자원을 할당받아서
프로세스는 프로그램을 수행하는데 필요한 데이터와 메모리 등의 자원과 쓰레드로 구성되어 있다.
무슨말?
프로세스 구성은 데이터 / 메모리 / 쓰레드 등이다 !!
쓰레드(Thread)란 ??
프로세스 내에서 실행되는 흐름의단위
cpu 스케줄링의 기본단위
쓰레드는 다음과 같은 특징을 가지고 있다.
- 쓰레드는 각자 자신의 Stack 영역을 보유한다. ( 최소한 자신의 레지스터 상태를 보유한다 )
- 쓰레드는 프로세스 내에서 Code, Data, Heap 영역을 공유한다.
- 쓰레드를 생성하고 switching 하는 것은 inexpensive(비싸지않은) 하다.
프로세스와 쓰레드는 무슨 차이가 있을까 ?
- 프로세스는 프로세스간의 통신에 IPC가 필요 !
- 쓰레드는 쓰레드간의 통신에 IPC 불필요 !
프로세스는 각각 code, data, heap, stack 영역을 보유햔다.
쓰레드는 stack 만 각각보유하고 code, data, heap은 공유한다.
프로세스는 생성과 context switching에 많은 비용이 들어간다.
쓰레드는 생성과 컨텍스트 스위칭에 적은 비용이 들어간다.
멀티 프로세스와 멀티 쓰레드는 무슨 차이가 있을까 ?
멀티 프로세싱은 ??
-여러개의 프로세스가 각자 하나의 작업(task)을 맡아 처리하는것을 뜻한다.
멀티쓰레딩은 ??
- 여러개의 쓰레드가 각자 하나의 작업을 맡아 처리하는것을 뜻한다.
-멀티 프로세싱 -
컨텍스트 스위칭이 발생하면 ?
-> 캐시에 존재하는 모든 데이터를 리셋하고 다시 캐시를 불러와야한다 !
== 오버헤드가 크기때문에 비용이 크다
프로세스간의 통신에 복잡한 IPC를 사용하여 통신해야한다.
여러개의 자식 프로세스중에 하나에 문제가 발생하면 ??
-> 그 자식 프로세스에만 이상이 생기고 다른 프로세스에는 영향을 주지 않는 장점이 존재한다.
-멀티 쓰레딩 -
시스템 자원소모가 감소한다 !
시스템콜이 줄어들어서 자원을 효율적으로 관리할수 있다
시스템콜 ?? 프로세스를 생헝하는것
시스템 처리량이 증가한다.
컨커런씨와 파라렐리즘을 얻을수 있기 때문에 효율적이다.
프로세스 내의 공유영역 덕분에 쓰레드간의 통신이 간단하다
그렇지만 쓰레드간의 자원을 공유하기 때문에 동기화 문제 ( synchronize problem ) 발생할수 있다.
디버깅 까다롭다
하나의 쓰레드에 문제 생기면 ? 전체 프로세스에 영향을 준다.
Thread 클래스 확장하기
첫번째 방법으로 java.lang.Thread 클래스를 확장할수 있습니다.
Thread 클래스에는 상당히 많은 메소드가 있는데 ,
그 중에서 run() 이라는 메소드만 오버라이드해주면 된다 !
import java.util.Random;
public class MyThread extends Thread {
private static final Random random = new Random();
@Override
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println("- " + threadName + " has been started");
int delay = 1000 + random.nextInt(4000);
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("- " + threadName + " has been ended (" + delay + "ms)");
}
}
run() 메소드를 @Override 해주었다 !
Runnable 인터페이스 구현하기
쓰레드 확장예제와 동일한기능을 Runnable 인터페이스를 구현하여 작성해 보았다.
클래스 이름 뒷 부분이
extends thread 에서 implements Runnable 로 바뀐거 빼고는
위에 예제랑 동일하다 !
import java.util.Random;
public class MyRunnable implements Runnable {
private static final Random random = new Random();
@Override
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println("- " + threadName + " has been started");
int delay = 1000 + random.nextInt(4000);
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("- " + threadName + " has been ended (" + delay + "ms)");
}
}
실행해보기
2가지 방법으로 작성한 쓰레드의 실행방법은 다르다 !
약간 다른데 뭐가 다르냐면,
쓰레드를 확장한 MyThread 클래스의 경우, 해당 객체에 start() 메소드를 직접 호출할수 있다.
반면에 Runnable 을 구현한 MyRunnable 클래스의 경우,
Runnable 형 인자를 받는 생성자를 통해 쓰레드 객체를 생성한 후에 start() 메소드를 호출해야한다.
public class ThreadRunner {
public static void main(String[] args) {
// create thread objects
Thread thread1 = new MyThread();
thread1.setName("Thread #1");
Thread thread2 = new MyThread();
thread2.setName("Thread #2");
// create runnable objects
Runnable runnable1 = new MyRunnable();
Runnable runnable2 = new MyRunnable();
Thread thread3 = new Thread(runnable1);
thread3.setName("Thread #3");
Thread thread4 = new Thread(runnable2);
thread4.setName("Thread #4");
// start all threads
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
}
아래는 실행결과이다.
4개의 쓰레드가 순차적으로 실행되지 않고, 랜덤딜레이 때문에 끝나는 시간도 각각 제각기이다.
그리고 매번 실행할때마다 딜레이가 달라지기 때문에, 실행결과가 항상동일하지 않을것이다 !!!
Thread vs Runnable
쓰레드 클래스를 확장하는것이 방법이 미세하게 더 간단하다는것을 볼수 있어용 !
하지만 자바에서는 다중상속을 허용하지 않지!
쓰레드 클래스를 확장하는 클래스는 다른클래스를 상속받을수 없어 !
반면에 runabble 인터페이스를 구현했을 경우 ? ??? ?? ??? ?
다른인터페이스를 구현할수 있을 뿐만 아니라 !
다른클래스도 상속받을수 있다
따라서 해당 클래스의 확장성이 중요한 상황이라면 ? ? ? ???? === Runnable 인터페이스를 구현하는것이 더 바람직할것이야 !
대부분 쓰레드를 확장하지말고 러너블을 구현하는방법을 택한다 !
부록, 람다를 이용하여 Tread 실행
러너블 인터페이스를 구현해서 해당클래스의 객체 생성하는 법보다 좀더 간단하게
Runnable 인터페이스를 사용할수 있습니다.
바로 람다를 사용하는것 !@!#!@#!@#!@#!@#
다음과 같이 Thread의 생성자의 인자로 람다를 넘기면 됩니다 !!
public class RunnableLambdaExample {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
String threadName = Thread.currentThread().getName();
System.out.println(threadName);
});
thread.setName("Thread #1");
thread.start();
}
}
다시한번 !
1. Runnable 인터페이스 구현하기 !
2. Thread 클래스를 상속하기 !
두 방법 모두 스레드를 통해 작업하고 싶은 내용을 run ( ) 메소드에 작성한다 !
class ThreadWithClass extends Thread {
public void run() {
for ( int i = 0; i<5; i++){
System.out.println(getNmae()); // 현재 실행중인 스레드의이름을 반환
try {
Thread.sleep(10); // 0.01초간 스레드를 멈춤 !
} catch ( InterruptedException e ) {
e.printStackTrace();
}
}
}
}
class ThreadWithRunnable implements Runnable {
public void run() {
for (int i =0; i<5; i++) {
System.out.println(Thread.currentThread().getName()); //현재 실행중인 스레드의 이름을 반환!
try {
Thread.sleep(10);
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Thread01 {
public static void main(String[] args) {
ThreadWithClass thread1 = new ThreadWithClass(); // Thread를 상속받는 방법
Thread thread2 = new Thread(new ThreadWithRunnable()); // Runnable 인터페이스 구현 !
thread1.start(); //스레드 실행
thread2.start(); //스레드 실행
}
}
쓰레드 클래스를 상속받으면 다른 클래스를 상속받을수 없으므로, 일반적으로 Runnable 인터페이스를 구현하는 방법으로 스레드를
생성한다.
Runnable 인터페이스는 몸체가 없는 메소드인
run()메소드 단 하나만을 가지는 인터페이스 이다 !
쓰레드의 상태
쓰레드 우선순위
자바에서는 각 스레드는 우선순위에 관한 자신만의 필드를 가지고 있습니다.
이러한 우선순위에 따라 특정 스레드가 더 많은시간 동안 작업을 할수 있도록 설정할수 있습니다.
getPriority()
setPriority() 메소드를 통해서 스레드의 우선순위를 반환하거나 변경할수 있습니다.
스레드의 우선순위가 가질수 있는 범위는 1부터 10까지 이며, 숫자가 높을수록 우선순위가 높아집니다 !!
하지만 스레드의 우선순위는 비례적인 절댓값이 아닌 어디까지나 상대적인값일 뿐 입니다.
우선순위가 10인 스레드가 우선순위가 1인 스레드보다 10 배 더빨리 수행되는것이 아닙니다.
단지 우선순위가 10인스레드는 우선순위가 1인스레드보다 좀더 많이 많이 실행큐에포함되어, 좀더 많은 작업시간을 할당받을뿐입니다.
그리고 스레드의 우선순위는 해당스레드를생성한 스레드의 우선순위를 상속받게 됩니다.
메인쓰레드
프로세스의 시작과 동시에 무조건 실행되는, 앱의 기본실행을 담당하는 스레드이다.
즉 프로세스의 최초스레드라고 이해하면된다.
추가로 동일한 프로세스 내에서 실행되는 모든 컴포넌트들은 메인스레드에서 인스턴스화 되며,
각 컴포넌트들에 대한 호출은 메인스레드에서 실행된다.
즉, 시스템 콜백에 응답하는 메소드들은???
--> 항상 프로세스의 메인스레드에서 실행된다.
워커쓰레드
우리가 작업을 하면서 주의할점은 서버통신이나 데이터베이스쿼리등의 긴 작업을 메인스레드에서 실행할경우 UI 가 차단된다.
스레드가 차단되면 UI 를 그리는 이벤트를 포함하여 모든 이벤트가 발송되지 않기 때문에
사용자에게 애플리캐이션이 중단된것처럼 보인다.
메인 스레드가 5초이상 차단되면 사용자에게 " 애플리케이션이 응답하지 않습니다 (ANR) " 라고 뜨는 오류가 발생 !
그럼 개발자는 UI 작업이 차단되지 않도록 하는데 용을 써야한다 !
그럼 도대체 어떻게 하나?
위에서 말했다시피 하나의
하나의 프로세스 안에 추가 스레드를 생성하고 조작할수있고
추가된 스레드를 워커스레드라고 한다 .
메인 스레드(Main Thread)
모든 자바 애플리케이션은 반드시 하나의 메인 스레드를 가지며, 메인 스레드가 main() 메소드를 실행하면서 시작된다. 메인 스레드는 main() 메소드의 마지막 코드를 실행하거나 return 문을 만나면 종료된다. 또한 메인 스레드는 여러 개의 작업 스레드를 생성(멀티 스레드)하여 병렬로 코드를 실행(멀티 태스킹)할 수 있다. 멀티 스레드 애플리케이션은 메인 스레드가 종료되더라도 아직 실행 중인 다른 스레드가 하나라도 있으면 프로세스를 종료시키지 않는다.
부정
그런건 없더라.
데드락
✔︎ 교착상태(Dead Lock)
프로세스(스레드)가 자원을 얻지 못해서 다음 처리를 하지 못하는 상태
✔︎ 교착 상태 발생 조건
- 상호배제(mutual exclusion)
자원은 한번에 한 프로세스(스레드)만 사용할 수 있음 - 점유 대기(hold and wait)
최소한 하나의 자원을 점유하고 있으면서 다른 프로세스(스레드)에 할당되어 사용하고 있는 자원을추가로 점유하기 위해 대기하는 프로세스(스레드)가 존재해야 함 - 비선점(no preemption)
다른 프로세스(스레드)에 할당된 자원은 사용이 끝날 때까지 강제로 빼앗을 수 없음 - 순환 대기(Circular wait)
프로세스(스레드)의 집합에서 순환 형태로 자원을 대기하고 있어야함
✔︎ 교착 상태 해결 방법
예방 & 회피
- 예방(prevention)
교착 상태 발생 조건 중 하나를 제거한다- 상호배제 부정: 여러 프로세스가 공유 자원 사용
- 점유대기 부정: 프로세스 실행전 모든 자원을 할당
- 비선점 부정: 자원 점유 중인 프로세스(스레드)가 다른 자원을 요구할 때 가진 자원을 반납
- 순환대기 부정: 자원에 고유번호 할당 후 순서대로 자원 요구
- 회피(avoidance)
교착 상태 발생 시 피해나가는 방법- 은행원 알고리즘
- 프로세스(스레드)가 자원을 요구할 때, 시스템은 자원을 할당한 후에도 안정 상태로 남아있게 되는지 사전에 검사하여 교착 상태를 회피한다
- 안정상태이며 자원을 할당하고 그렇지 않으면 다른 프로세스(스레드)가 자원 해지까지 대기
- 은행원 알고리즘
탐지 & 회복
- 탐지(detection)
교착 상태를 탐지한다(자원 요청 시, 탐지 알고리즘을 실행시켜 그에 대한 오베헤드 발생) - 회복(recovery)
교착 상태를 일으킨 프로세스(스레드)를 종료하거나, 할당된 자원을 해제시켜 회복시키는 방법- 프로세스 종료 방법
- 교착 상태의 프로세스(스레드)를 모두 중지
- 교착 상태가 제거될 때까지 하나씩 프로세스(스레드) 중지
- 자원 선점 방법(선점: 자원을 강제로 뺏음)
- 교착 상태의 프로세스(스레드)가 점유하고 있는 자원을 선점
- 우선 순위 낮은 프로세스(스레드)나 수행 횟수가 적은 프로세스(스레드) 위주로 자원 선점
- 프로세스 종료 방법
'백기선 자바스터디' 카테고리의 다른 글
백기선 자바스터디 11주차 (0) | 2023.02.02 |
---|---|
백기선 자바스터디 08주차 (0) | 2023.01.31 |
백기선 자바스터디 09주차 (0) | 2023.01.31 |
백기선 자바스터디 07주차 (0) | 2023.01.31 |
백기선 자바스터디 06주차 (0) | 2023.01.30 |