스레드를 알아보기 전에 동기식 처리 방식과 비동기식 처리 방식을 알아야합니다.
생각 정리
동기식 처리 방식
프로세스 하나가 끝야 다음 프로세스로 넘어가는 것이 동기식 처리 방식이다.
제조업에서 조립공정1 이 끝나야 다음 조립공정2로 넘어가는 것으로 비유하면 좋겠네요.
문제점은 조립공정1의 공정 시간이 오래 걸릴수록 다음공정이 계속 대기한다는 것이 문제점입니다.
그러므로 완조립체가 늦게 나오는 문제점이 생길 수 있겠죠?
동기식 처리 방식 예제 코드
1부터 100까지 출력해주는 예제 코드이다. 자식 메서드 2개를 만들고 메인 프로세스를 1개를 만들어 찍어보자.
print1 이 먼저 처리가 되고 다음 print2 , main 순서대로 처리가 끝나면 다음 순서를 처리 할 것이다.
결과는 생략..
비동기식 처리 방식
동기식과는 달리 시분할 처리 방법으로 문맥 스위칭을 하며 프로세스들을 처리한다.
멀티 태스킹을 하여 여러 프로세스들을 왔다갔다하며 일을 처리하는 것인데 우리는 이 처리를 볼때 속도가 빨라서 동시에 동작하고 있다는 것처럼 볼 수도 있다.
이 처리의 문제점
1. 처리 할 프로세스 일이 많으면 시간 분배에 형편성이 안맞는다.
2. 일 처리를 왔다갔다하는 해야하는 시간 즉, 스위칭 시간이 길어진다.
3. 프로세스 즉, 일처리하는 기능들의 데이터 공유가 어렵다.
일반적인 비동기식 처리 방식의 문제점을 개선
Thread ( 스레드 )
코드로 이해 해보자.
자식 프로세스
메인 프로세스
Thread 를 사용한 비동기식 처리 방식.
실행을 시키면 랜덤하게 메인이 먼저 실행이 되거나 자식이 먼저 실행이 될 것이다.
결과는 생략..
Thread 의 기능 소개
- start() : 스레드를 실행하는 기능
- sleep() : 일정 시간 동안 스레드 정지 시키는 기능
- join() : 메인 프로세스가 자식 보다 먼저 끝났을 경우 일정 시간동안 기다려주게 해주는 기능
- interrupt() : 메인 프로세스가 끝났는데 자식 프로세스가 실행 중 일 경우 종료를 요청하여 종료시는 기능
- currentThread() : 스레드의 정보를 얻을수 있게 해줌
- Id() : 스레드의 아이디
- Name() : 스레드의 이름
- Priority() : 스레드의 우선순위
- state() : 스레드의 상태
join() 메서드 사용법
예외 처리를 해주어야 에러가 안납니다.
interrupt() 메서드 사용법
isInterrupted() 메서드로 스레드를 종료 시켜도 되냐는 요청을 주고 interrupte() 메서드를 사용해 예외 발생 후 종료를 시킵니다.
Thread 상태의 변화
처음 생성 되었을 때 NEW 상태
그다음 RUNNABLE 상태
다음 실행 상태로 넘어간다.
코드를 보자.
현재 이 스레드의 상태는 실행 전이기에 NEW 상태 일 것이다.
다음
스레드를 시작하고 상태를 보자.
지금 상태는 RUNNABLE 상태 일 것이다.
여기서 sleep() 이라는 메서드를 이용해보자.
sleep() 메서드를 사용하면 해당 스레드는 다른 공간에서 잠시 쉬었다가 다시 RUNNABLE 상태로 갈 것이다.
Life Cycle 그림
정리
생성(New) -> 실행대기(Runnable) -> 실행 -> 소멸
이순서대로 되는데 실행에서 우리는 스레드를 일정 기간 동안 정지 시킬 수 있는데, 정지 시키면 이 스레드는 Watting이라는 공간으로 들어가게 된다. 이 공간 구조는 Queue 구조랑 똑같습니다. 먼저 들어온 사람이 먼저 나가는 구조입니다.
Priority() 메서드
1~10의 범위 값을 설정 하여 우선순위를 정하는 메서드이다.
큰 숫자일수록 우선순위가 높다.
스레드 2번을 우선순위 10을 설정 해줬으니 작업 끝나는 순위를 2번이 끝나게 해주겠죠??
Daemon 스레드 (유령 스레드)
업무를 가지고 있는 프로세스를 보조적인 스레드이고, 프로세스가 작업이 끝나면 데몬 스레드도 할 일이 없기 Java 의 장점 가비지 컬렉터에 의해 종료를 시켜줍니다.
데몬 스레드를 사용하려면 다른 보조적인 스레드를 생성하여 사용해야 한다.
스레드 그룹이용하기
ThreadGrop 객체를 사용해 그룹을 만들어 사용할 수도 있습니다.
스레드 동기화
여러 스레드가 같은 프로세스 내의 자원을 공유해서 작업하기 때문에 서로의 작업에 영향을 주게 됩니다.
JVM 메모리 구조를 생각해봅시다.
Heap 영역에는 공통된 자원과 객체가 생성 된 것이 들어가게 됩니다.
Stack 영역에는 메서드들이 들어가게 되있습니다. 여기서 Stack 영역에서의 각자 자원은 문제가 되지 않지만 공통된 자원 즉, Heap 영역에 들어 있는 자원이 스레드의 미미한 시간 차이 때문에 빠질 수도 있고 중복 될 수 있는 문제점이 생깁니다.
문제점을 해결하기 위해 우리는 동기화라는 것을 사용하게 되고,
잠금장치 자물쇠인 synchronized 사용하여 임계영역 ( critical section ) 을 만들어 줍니다.
화장실을 비유해보면
내가 먼저 도착해서 문을 잠그고 일을보고 있으면 다른 사람들은 도착하면 내가 일을 다 보길 기도하며 기다리고 있을겁니다.
여기서 잠금장치된 화장실이 2개 있다고 가정해봅시다.
2개의 화장실을 여러 사람들이 사용하면 작업이 빠르지 않을까? 라는 생각이 듭니다.
그런데 동기화 특성상 화장실이 2개가 있다고 2개를 동시에 사용을 못합니다.
먼저 도착한 사람이 도착하여 잠궈버리면 나머지 화장실도 잠겨 버리고 나머지 사람들은 강제적으로 기다려야 하게 되는 문제점이 또 발생됩니다.
문제점을 해결하기 위해 ReentreantLock 이라는 것을 사용합니다. 이것은 화장실을 기다리는 사람들이 첫 번째 사람이 일을 다 봐야할때까지 무조건 기다려야하는 문제점이 생겨나 첫 번째 사람이 일을 볼 떄 대기하는 사람들은 밖에 나가서 다른 작업을 하다가 첫 번째 사람이 일을 다보면 그 다음 사람이 들어가는 방식으로 기존의 동기화 방식의 문제점을 해결해줍니다.
'Laguage' 카테고리의 다른 글
[Java] Java 에서 파일을 읽는 여러가지 방법 (0) | 2022.06.26 |
---|---|
[Java] What is Serialization in Java? (0) | 2022.06.24 |
[Java] ArchUnit 아키텍처 테스트에 대해 알아보자 (0) | 2022.06.18 |
[JAVA] JVM 구조 (0) | 2022.02.11 |
[JAVA] Optional (0) | 2022.01.20 |
[JAVA] @애노테이션 ( Annotation ) (0) | 2021.12.28 |
[JAVA] 열거형 ( enums ) (0) | 2021.12.28 |
[java] 객체지향 프로그래밍 설계 원칙 - SOLID (0) | 2021.12.10 |