Thread
프로세스
컴퓨터에서 실행 중인 애플리케이션을 의미하며,
실행 시 운영체제로부터 필요한 만큼의 메모리를 할당받아 프로세스가 됨.
프로세스는 데이터, 컴퓨터 자원, 스레드로 구성되어 있음.
스레드는 프로세스 내에서 실행되는 소스 코드의 실행 흐름을 의미함.
main
메서드이며, 메인 스레드가 main
메서드를 실행시킴.main
메서드의 코드를 처음부터 끝까지 순차적으로 실행시키며, 코드의 끝을 만나거나 return문을 만나면 실행 종료.작업 스레드 생성과 실행
작업 스레드가 수행할 코드를 작성하고, 작업 스레드를 생성하여 실행시키는 것.
run
메서드 내에 스레드가 처리할 작업을 작성하도록 규정되어 있음.Runnable
인터페이스를 구현한 객체에서 run()
을 구현하는 방법public class ThreadExample1 { public static void main(String[] args) { // Runnable 인터페이스를 구현한 객체 생성 Runnable task1 = new ThreadTask1(); // 구현 객체를 인자로 전달하면서, Thread 클래스를 인스턴스화 하여 객체 생성 Thread thread1 = new Thread(task1); ⠀⠀⠀ thread1.start(); // 작업 메서드 실행 ⠀⠀⠀ // 메인 메서드가 수행할 코드 for (int i = 0; i < 30; i++) { System.out.print("@"); } } } // Runnable 을 구현하는 임의의 클래스 생성⠀⠀⠀ class ThreadTask1 implements Runnable { public void run() { // 반드시 run()을 구현해야함. for (int i = 0; i < 30; i++) { System.out.print("#"); } // 작업 스레드가 수행할 코드 } } // 출력 (실제로는 개행없이 일렬로 출력됨) // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // ############################## // i의 범위를 늘릴경우 @,#이 교차출력되며, 매번 실행 결과가 조금씩 달라짐. /* 메인 스레드와 작업 스레드가 동시에 병렬로 실행되면서, main 메서드와 run()메서드의 코드가 실행됨. */
// 1-1 익명 Runnable 구현 객체를 활용하여 스레드를 생성하는 방법 public class ThreadExample1 { public static void main(String[] args) { Thread thread1 = new Thread(new Runnable() { public void run() { for (int i = 0; i < 100; i++) { System.out.print("#"); } } }); ⠀⠀⠀ thread1.start(); ⠀⠀⠀ for (int i = 0; i < 100; i++) { System.out.print("@"); } } }
Thread
클래스를 상속 받은 하위 클래스에서 run()
을 구현하는 방법public class ThreadExample2 { public static void main(String[] args) { // Thread 클래스를 상속받은 클래스를 인스턴스화 하여 객체 생성 Thread thread2 = new ThreadTask2(task1); ⠀⠀⠀ thread2.start(); // 작업 메서드 실행 ⠀⠀⠀ // 메인 메서드가 수행할 코드 for (int i = 0; i < 30; i++) { System.out.print("@"); } } } // Thread을 상속받는 하위 클래스 생성⠀⠀⠀ class ThreadTask2 extends Thread { public void run() { // 반드시 run()을 구현해야함. for (int i = 0; i < 30; i++) { System.out.print("#"); } // 작업 스레드가 수행할 코드 } } // 출력은 runnable 과 유사함. /* 메인 스레드와 작업 스레드가 동시에 병렬로 실행되면서, main 메서드와 run()메서드의 코드가 실행됨. */
// 2-1 익명 Thread 하위 객체를 활용하여 스레드를 생성하는 방법 public class ThreadExample2 { public static void main(String[] args) { Thread thread2 = new Thread() { public void run() { for (int i = 0; i < 100; i++) { System.out.print("#"); } } }; ⠀⠀⠀ thread2.start(); ⠀⠀⠀ for (int i = 0; i < 100; i++) { System.out.print("@"); } } }
스레드의 이름
메인스레드는 main
, 추가적으로 생성한 스레드는 기본적으로 Thread-n
사용.
스레드의_참조값.getName()
public class GetThreadName { public static void main(String[] args) { Thread thread3 = new Thread() { public void run() { System.out.println("Thread Name"); } }; thread3.start(); ⠀⠀⠀ System.out.println("thread3.getName() = " + thread3.getName()); } } /* 출력 Thread Name thread3.getName() = Thread-0 */
스레드의_참조값.setName()
public class SetThreadName { public static void main(String[] args) { Thread thread4 = new Thread() { public void run() { System.out.println("Set Thread Name"); } }; thread4.start(); System.out.println("thread4.getName() = " + thread4.getName()); thread4.setName("t4"); System.out.println("thread4.getName() = " + thread4.getName()); } } /* 출력 Set Thread Name thread4.getName() = Thread-0 thread4.getName() = t4 */
Thread.currentThread()
public class ThreadAddress { public static void main(String[] args) { Thread thread5 = new Thread() { public void run() { System.out.println(Thread.currentThread().getName()); } }; thread5.start(); System.out.println(Thread.currentThread().getName()); } } /* 출력 main Thread-0 */
스레드의 동기화
멀티 스레드 프로세스의 두 스레드가 동일한 데이터를 공유하면서 문제가 발생할 수 있기 때문에, 이를 막기 위해 스레드 동기화를 해야 함.
synchronized
synchronized
키워드 작성하여 메서드 전체를 임계 영역으로 설정할 수 있음.// 이해를 돕기 위한 예시 코드 class Account { ... /* withdraw()메서드 호출시 실행하는 스레드는 withdraw()가 포함된 객체의 락을 얻으며, 해당 스레드가 락을 반납하기 이전에 다른 스레드는 해당 메서드의 코드를 실행하지 못함 */ public synchronized boolean withdraw(int money) { if (balance >= money) { try { Thread.sleep(1000); } catch (Exception error) {} balance -= money; return true; } return false; } }
synchronized
synchronized
키워드와 함께 소괄호()
안에 해당 영역이 포함된 객체를 넣고, 중괄호 {}
로 블럭을 열어, 블럭 내에 코드를 작성.class Account { ... public boolean withdraw(int money) { synchronized (this) { if (balance >= money) { try { Thread.sleep(1000); } catch (Exception error) {} balance -= money; return true; } return false; } } }
스레드의 상태 / 실행제어
start()
Thread.sleep(long milliSecond)
Thread.sleep(1000);
와 같이 클래스를 통해 호출하는 것을 권장.interrupt()
호출.interrupt()
사용시, try-catch 문으로 sleep
을 감싸서 사용.interrupt()
sleep()
, wait()
, join()
으로 일시 정지 상태인 스레드를 실행 대기 상태로 복귀멈춰 있는 스레드.interrupt()
호출시, 메서드(sleep, wait, join)에서 예외 발생, 이에 따른 일시 정지 해제.yield()
yield()
를 호출하면 남은 2초는 다음 스레드에게 양보.public void run() { while (true) { // else 문이 없을 경우 example==false여도 무의미한 while문 반복. if (example) { ... } // 실행대기 상태로 바뀌며, 자신에게 남은 시간을 양보. else Thread.yield(); } }
join(long milliSecond)
interrupt()
호출 / 호출 시 지정했던 다른 스레드가 모든 작업 마침.sleep
과 상당 부분 유사하나, join
은 특정 스레드에 대해 동작하는 인스턴스 메서드인 점에서 차이를 보임. 특정스레드.join();
의 형태로 호출.wait()
notify()
// 스레드A와 스레드B가 공유 객체를 두고 협업하는 상황 1. 스레드A가 공유 객체에 자신의 작업을 완료 2. 스레드B와 교대하기 위해 notify()를 호출 3. 스레드B가 실행 대기 상태가 되며, 곧 실행됨. 4. 스레드A는 wait()을 호출하며 자기 자신을 일시 정지 상태로 만듦 5. 스레드B가 작업을 완료하면 notify()를 호출하여 작업을 중단 6. 스레드A를 다시 실행 대기 상태로 복귀시킴. 7. 스레드B는 wait()을 호출하며 자기 자신을 일시 정지 상태로 만듦 // 두 스레드는 공유 객체에 대해 서로 배타적으로 접근하며 협업
JVM
JVM(Java Virtual Machine)은 자바 프로그램을 실행시키는 도구
자바로 작성한 소스 코드를 해석해 실행하는 별도의 프로그램
특징
소스코드 실행 flow
.java
에서 .class
(바이트 코드 파일)로 확장자 변환 Rumtime Data Area
)Class Loader
)가 바이트 코드 파일을 JVM 내부로 불러들여 런타임 데이터 영역에 적재. 소스 코드를 메모리에 로드.Stack / Heap
Method area Heap area Stack area PC
registerNative
method stack
빈 stack -> -> 메서드2 out 호출된 메서드2 완료된 메서드2 ⠀⠀⠀⠀⠀⠀⠀⠀⠀ 호출된 메서드1 호출된 메서드1 호출된 메서드1
Heap
영역에 생성되며, 인스턴스가 생성된 위치의 주소값을 참조변수에게 할당. (참조변수는 Stack 영역에 선언됨)Garbage Collection
프로그램에서 더 이상 사용하지 않는 객체를 찾아 삭제하거나 제거하여 메모리를 확보하는 것
Student student = new Student(); student.setName("페퍼"); student = null; // 인스턴스와 참조변수 간의 연결이 끊김, 가비지 발생 student = new Student(); student.setName("민트");
Heap Heap Heap Heap Minor GC
활동영역Major GC
활동영역Eden Survival 0 Survival 1 Old Young Young Young Old
- Stop The World
- 가비지 컬렉션을 실행시키기 위해 JVM이 애플리케이션의 실행을 멈추는 작업
- 가비지 컬렉션이 실행될때 가비지 컬렉션을 실행하는 스레드를 제외한 모든 스레드들의 작업은 중단되고, 가비지 정리가 완료되면 재개됨.
- Mark and Sweep
Mark
사용되는 메모리와 사용하지 않는 메모리를 식별하는 작업Sweep
은 사용되지 않음으로 식별된 메모리를 해제하는 작업.