Backend boot camp/Session1
[JAVA] 심화 - Thread, JVM
by orioncsy
2022. 9. 19.
0. 스레드
개요
학습 목표
- 스레드란
- 싱글 스레드 VS 멀티 스레드
- 스레드 생성 방법
- 스레드 실행 및 동기화
- 스레드 상태 및 제어
스레드란?
프로세스
- 운영체제가 할당한 메모리에 실행 중인 애플리케이션
- 데이터, 컴퓨터 자원, 스레드로 구성
스레드
- 프로세스 내 소스 코드의 실행 흐름
- 멀티 스레드로 동시 작업 실행 가능
메인 스레드(Main thread)
- 자바 애플리케이션에서 메인 메서드를 가장 먼저 실행
- 메인 메서드 안의 코드를 실행시키는 메인 스레드
- 메인 스레드만 사용하면 싱글 스레드
멀티 스레드(Multi thread)
- 여러 개의 스레드가 동시 작업
- 하나의 애플리케이션 내 여러 작업을 동시에 실행
스레드 생성과 실행
Runnable 인터페이스에서 run() 메서드를 구현하여 스레드 생성
public class Main{
public static void main(String[] args) {
Thread thread1 = new Thread(new Task());
// Runnable runnable = new Task();
// Thread thread1=new Thread(runnable); // 따로 선언하여 사용 가능
thread1.start(); // thread 작업 시작
for(int i=0; i<200; i++)
System.out.print("main_thread");
}
}
class Task implements Runnable{
@Override
public void run() {
for(int i=0; i<200; i++)
System.out.print("thread1");
}
}
//실행 결과 병렬적으로 작업을 처리하는 것 확인
익명 클래스를 활용하여 Runnable 인터페이스에서 run() 메서드를 구현
public class Main{
public static void main(String[] args) {
Thread thread1 = new Thread(new Runnable(){
@Override
public void run() {
for(int i=0; i<200; i++)
System.out.print("thread1");
}
});
// Runnable runnable = new Task();
// Thread thread1=new Thread(runnable); // 따로 선언하여 사용 가능
thread1.start(); // thread 작업 시작
for(int i=0; i<200; i++)
System.out.print("main_thread");
}
}
Thread 클래스를 상속 받은 하위 클래스에서 run() 구현하여 스레드 생성
public class Main{
public static void main(String[] args) {
Thread thread2=new Task2();
thread2.start();
for(int i=0; i<200; i++)
System.out.print("main_thread");
}
}
class Task2 extends Thread{
@Override
public void run() {
for(int i=0; i<200; i++)
System.out.print("thread1");
}
}
Thread 익명 하위 객체를 이용한 스레드 생성
public class Main{
public static void main(String[] args) {
Thread thread2=new Task2(){
@Override
public void run() {
for(int i=0; i<200; i++)
System.out.print("thread1");
}
};
thread2.start();
for(int i=0; i<200; i++)
System.out.print("main_thread");
}
}
스레드 이름
- 메인 스레드는 main, 그 외의 스레드는 thread-n의 이름을 가진다
스레드 이름 조회
public class Main{
public static void main(String[] args) {
Thread thread1 = new Thread(new Runnable(){
@Override
public void run() {
System.out.println("get thread name..");
}
});
thread1.start(); // thread 작업 시작
System.out.println(thread1.getName());
}
}
스레드 이름 설정
public class Main{
public static void main(String[] args) {
Thread thread1 = new Thread(new Runnable(){
@Override
public void run() {
System.out.println("set thread name");
}
});
thread1.start(); // thread 작업 시작
thread1.setName("0th thread");
System.out.println(thread1.getName());
}
}
스레드 인스턴스의 주소값
public class Main{
public static void main(String[] args) {
Thread thread1 = new Thread(new Runnable(){
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
thread1.start(); // thread 작업 시작
System.out.println(Thread.currentThread().getName());
//현재 thread의 인스턴스 주소값을 반환(Thread.currentThread())
}
}
스레드 동기화
- 스레드 동기화
- 두 개 이상의 스레드가 같은 데이터를 공유하여 발생하는 문제를 해결
- synchronized()로 임계 영역(critical section)을 설정하고 하나의 스레드만 실행 가능한 구역으로 만든다
- 해당 임계 영역에 접근할 수 있는 권한인 lock을 획득한 스레드만 임계 영역을 실행 가능
- 스레드 동기화 방식
- 메서드 전체를 임계 영역으로 설정
- public synchronized void method(){…}
- 특정 영역을 임계 영역으로 설정
스레드 상태 및 제어
- .start()는 스레드를 실행 대기 상태로 올린다
- 스레드의 상태가 존재하고 제어하는 메서드들 존재
스레드 상태
- 스레드 생성(new)
- 실행 대기(runnable)
- 실행
- 일시정지
- 소멸
스레스 실행 제어 메서드
- static void sleep(long millisecond) : millisecond 동안 스레드 일시정지
- void interrupt() : 실행 대기로 복귀
- wait, sleep, join으로 일시 정지한 스레드를 실행 대기 상태로 복귀
- wait, sleep, join의 예외가 발생하여 일시정지 해제
- 예제
public class Main{
public static void main(String[] args) {
Thread thread1 = new Thread(new Runnable(){
@Override
public void run() {
try{
while(true){
Thread.sleep(1000);
// 1초 단위로 계속해서 thread를 TIMED_WAITING상태로 만든다
}
}
catch (Exception e){
System.out.println("interrupted now");
//interrupt로 대기 상태로 복귀되었을 때 실행
}
}
});
System.out.println(thread1.getState()); // 스레드 생성(NEW) 상태
thread1.start(); // thread 작업 시작
while(true){
if(thread1.getState() == Thread.State.TIMED_WAITING) {
System.out.println(thread1.getState());
break; // 계속해서 TIMED_WAITING상태로 존재한다
}
}
thread1.interrupt(); // thread를 실행 대기(RUNNABLE)상태로 복귀
while(true){
if(thread1.getState() == Thread.State.RUNNABLE) {
System.out.println(thread1.getState());
break;
}
} // 복귀 상태 RUNNABLE 상태 출력
while(true) {
if (thread1.getState() == Thread.State.TERMINATED) {
System.out.println(thread1.getState());
break;
}
}//실행되고 나서 소멸 상태 출력
}
}
- static void yield() : 다른 스레드에게 실행 양보
- 실행 상태에서 다른 스레드에게 실행을 양보
- 예제
public void run(){
while(true){
if(...){
...
}
else
Thread.yield(); //(무의미한 반복문이 반복될 때 다른 스레드에게 실행 양보)
}
}
- void join() / void join(long millisecond) : 다른 스레드의 작업이 끝날 때까지 기다림
- sleep()과 유사하지만 join()은 특정 스레드에 대하여 작동하는 인스턴스 메서드다
- notify(), wait() : 두 스레드가 교대로 작업할 때 사용
- notify()로 다른 스레드를 실행 대기 상태로 두고 wait로 자신을 일시정지로 바꾸며 교대
public class Main{
public static void main(String[] args) {
Working working = new Working();
Thread1 thread1= new Thread1(working);
Thread2 thread2= new Thread2(working);
thread1.start();
thread2.start();
//출력 결과는 this is method1/ this is method2가 교대하며 20번씩 반복
}
}
class Working{
public synchronized void method1(){
System.out.println("this is method1");
notify();
try {
wait();
} catch (Exception e) {};
}
public synchronized void method2(){
System.out.println("this is method2");
notify();
try {
wait();
} catch (Exception e) {};
}
}
class Thread1 extends Thread{
Working working;
public Thread1(Working working) {
this.working = working;
}
@Override
public void run() {
for(int i=0; i<20; i++)
working.method1();
}
}
class Thread2 extends Thread{
Working working;
public Thread2(Working working) {
this.working = working;
}
@Override
public void run() {
for(int i=0; i<20; i++)
working.method2();
}
}
1. 자바 가상 머신(Java Virtual Machine)
개요
Write once, run anywhere
- JAVA는 JVM을 통해 운영체제에 독립적으로 프로그래밍 가능
학습 목표
- JVM이란?
- JVM 메모리 구조
- Garbage Collection
JVM이란?
Java Virtual Machine
- 자바로 작성된 소스 코드를 실행하는 프로그램
- 프로그램이 실행되기 위해서 컴퓨터 자원을 요청
- 운영체제마다 방식이 다름
- java는 JVM을 통해 각 운영체제에 맞게 변환시켜준다
JVM 구조
- java source code(.java)
- java Byte code(.class file)
- java 파일을 javac라는 명령어로 java compiler를 사용하여 변환
- JVM
- runtime data area : 소스 코드에 필요한 메모리 할당
- class loader : java Byte code를 runtime data area에 적재
- execution engine : runtime data area에 적재된 byte code를 실행
- interpreter : 코드를 한 줄씩 기계어로 번역하고 실행
- JIT(just-in-time) Compiler : 전체 코드를 기계어로 번역하고 실행
- 기본적으로 interpreter를 사용하고 반복되는 코드를 JIT로 실행
Stack과 Heap
Java 메모리 구조
- Runtime data area
- method area
- heap area
- stack area
- PC register
- Native method stack
Stack
- LIFO(Last In First Out) 방식의 자료구조
- JVM 내 stack
- 메서드가 실행되면 method frame이 생성
- 매겨변수, 지역변수, 연산 값 등이 스택 형태로 저장
- 메서드가 종료될 때 역순으로 제거된다
Heap
- JVM 내 하나의 heap 영역 존재
- 인스턴스를 생성하여 저장하는 장소
Garbage Collection
Garbage Collection
- 메모리 자동 관리 프로세스
- 더 이상 사용하지 않는 객체를 삭제
- 아무도 참조하고 있지 않는 객체를 제거
동작 방식
- 힙 영역 내에 객체가 얼마나 살아있냐에 따라 young, old 방식 존재
- Young 영역
- 새롭게 생성된 객체가 할당되고 생성, 삭제가 반복되는 곳
- Minor GC라고 부름
- Old 영역
- 상태를 유지하고 남은 객체들이 복사되는 곳으로 크기가 크고 삭제할 데이터가 적다
- Major GC라고 부름
- 기본적인 동작 방식
- Stop The World
- GC를 실행하기 위한 스레드를 제외한 모든 애플리케이션 실행을 중단
- GC가 완료되면 재개
- Mark and Sweap
- 메모리가 사용되고 있는지 식별(Mark)하고
- 사용되지 않는다면 제거(Sweep)한다