0310 TIL 정리 및 요약
public class ThreadExample2 {
public static void main(String[] args) {
ThreadTask2 thread2 = new ThreadTask2();
// 작업 스레드를 실행시켜, run() 내부의 코드를 처리하도록 합니다.
thread2.start();
// 반복문 추가
for (int i = 0; i < 100; i++) {
System.out.print("@");
}
}
}
class ThreadTask2 extends Thread {
public void run() {
for (int i = 0; i < 100; i++) {
System.out.print("#");
}
}
}
프로세스: 실행중인 애플리케이션이다. 데이터, 컴퓨터 자원, 스레드로 구성된다.
스레드: 프로세스내에서 실행되는 소스 코드의 실행 흐름
run() : Runnable 인터페이스와 Thread 클래스에 정의
run()
run()
을 구현run()
run() 메서드 바디에 스레드가 수행할 작업 내용 작성
class ThreadTask1 implements Runnable {
// run() 메서드 바디에 스레드가 수행할 작업 내용 작성
public void run() {
for (int i = 0; i < 100; i++) {
System.out.print("#");
}
}
}
public class ThreadExample1 {
public static void main(String[] args) {
// Runnable 인터페이스를 구현한 객체 생성
Runnable task1 = new ThreadTask1();
// Runnable 구현 객체를 인자로 전달하면서 Thread 클래스를 인스턴스화하여 스레드를 생성
Thread thread1 = new Thread(task1);
// 위의 두 줄을 아래와 같이 한 줄로 축약할 수도 있습니다.
// Thread thread1 = new Thread(new ThreadTask1());
}
}
class ThreadTask1 implements Runnable {
public void run() {
for (int i = 0; i < 100; i++) {
System.out.print("#");
}
}
}
public class ThreadExample1 {
public static void main(String[] args) {
Runnable task1 = new ThreadTask1();
Thread thread1 = new Thread(task1);
// 작업 스레드를 실행시켜, run() 내부의 코드를 처리하도록 합니다.
thread1.start();
// 차이 확인을 위해 예제코드 추가
for (int i = 0; i < 100; i++) {
System.out.print("@");
}
}
class ThreadTask1 implements Runnable {
public void run() {
for (int i = 0; i < 100; i++) {
System.out.print("#");
}
}
}
run()
을 구현run()
메서드가 정의되어져 있으며, 따라서 run()
메서드를 오버라이딩.public class ThreadExample2 {
public static void main(String[] args) {
ThreadTask2 thread2 = new ThreadTask2();
// 작업 스레드를 실행시켜, run() 내부의 코드를 처리하도록 합니다.
thread2.start();
// 반복문 추가
for (int i = 0; i < 100; i++) {
System.out.print("@");
}
}
}
class ThreadTask2 extends Thread {
public void run() {
for (int i = 0; i < 100; i++) {
System.out.print("#");
}
}
}
1번만 사용
하려고 할 때 쓴다.public class ThreadExample1 {
public static void main(String[] args) {
// 익명 Runnable 구현 객체를 활용하여 스레드 생성
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("@");
}
}
}
public class ThreadExample2 {
public static void main(String[] args) {
// 익명 Thread 하위 객체를 활용한 스레드 생성
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”이라는 이름을 가진다.
public class ThreadExample3 {
public static void main(String[] args) {
Thread thread3 = new Thread(new Runnable() {
public void run() {
System.out.println("Get Thread Name");
}
});
thread3.start();
System.out.println("thread3.getName() = " + thread3.getName());
}
}
출력 결과
thread3.getName() = Thread-0
Process finished with exit code 0
스레드의_참조값.setName()
public class ThreadExample4 {
public static void main(String[] args) {
Thread thread4 = new Thread(new Runnable() {
public void run() {
System.out.println("Set And Get Thread Name");
}
});
thread4.start();
System.out.println("thread4.getName() = " + thread4.getName());
thread4.setName("Code States");
System.out.println("thread4.getName() = " + thread4.getName());
}
}
currentThread()
를 사용ublic class ThreadExample1 {
public static void main(String[] args) {
Thread thread1 = new Thread(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
thread1.start();
System.out.println(Thread.currentThread().getName());
}
}
출력결과
/Library/Java/JavaVirtualMachines/zulu-11.jdk/Contents/Home/bin/java -javaagent:/Users/0hyun.cho/Library/Application Support/JetBrains/Toolbox/apps/IDEA-C/ch-0/221.5080.210/IntelliJ IDEA CE.app/Contents/lib/idea_rt.jar=52293:/Users/0hyun.cho/Library/Application Support/JetBrains/Toolbox/apps/IDEA-C/ch-0/221.5080.210/IntelliJ IDEA CE.app/Contents/bin -Dfile.encoding=UTF-8 -classpath /Users/0hyun.cho/study/example/out/production/classes ThreadExample1
main
Thread-0
Process finished with exit code 0
멀티 스레드 프로세스의 경우, 두 스레드가 동일한 데이터를 공유하게 되어 문제가 발생할 수 있습니다.
try { Thread.sleep(1000); } catch (Exception error) {}
try { Thread.sleep(1000); } catch (Exception error) {}
Thread.sleep(1000);
Thread.sleep()
은 반드시 try … catch
문의 try
블럭 내에 작성해주어야 한다.try { … } catch ( ~ ) { … }
try … catch
문은 예외 처리에 사용되는 문법이다.try
의 블록 내의 코드를 실행하다가 예외 또는 에러가 발생하면 catch
문의 블럭에 해당하는 내용을 실행하라는 의미가 된다.Thread.sleep(1000);
의 동작을 위해 형식적으로 사용한 문법 요소.임계 영역은 오로지 하나의 스레드만 코드를 실행할 수 있는 코드 영역을 의미
하며, 락은 임계 영역을 포함하고 있는 객체에 접근할 수 있는 권한을 의미한다.
특정 코드 구간을 임계 영역으로 설정할 때에는 synchronized
라는 키워드를 사용한다. synchronized
키워드는 두 가지 방법으로 사용할 수 있다.
synchronized
키워드를 작성하면 메서드 전체를 임계 영역으로 설정할 수 있다.synchronized
키워드와 함께 소괄호(()
) 안에 해당 영역이 포함된 객체의 참조를 넣고, 중괄호({}
)로 블럭을 열어, 블럭 내에 코드를 작성class Account {
...
public synchronized boolean withdraw(int money) {
if (balance >= money) {
try { Thread.sleep(1000); } catch (Exception error) {}
balance -= money;
return true;
}
return false;
}
}
this
에 해당하는 객체의 락을 얻고, 배타적으로 임계 영역 내의 코드를 실행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;
}
}
}
C++의 문제점: os로부터 비독립적인 특징. > 이걸 해결하기 위해 자바가 만들어졌고, JVM이라는 프로그램을 통해 구현된다.
자바로 소스 코드를 작성하고 실행하면, 먼저 컴파일러가 실행되면서 컴파일이 진행된다. 컴파일의 결과로 .java 확장자를 가졌던 자바 소스 코드가 .class 확장자를 가진 바이트 코드 파일로 변환된다.
이후, JVM은 운영 체제로부터 소스 코드 실행에 필요한 메모리를 할당받고 그것이 바로 위 그림 상의 런타임 데이터 영역(Rumtime Data Area)이다.
그 다음에는 클래스 로더(Class Loader)가 바이트 코드 파일을 JVM 내부로 불러들여 런타임 데이터 영역에 적재시킨다. 자바 소스 코드를 메모리에 로드시키는 것.
로드가 완료되면 이제 실행 엔진(Execution Engine)이 런타임 데이터 영역에 적재된 바이트 코드를 실행.
이 때, 실행 엔진은 두 가지 방식으로 바이트 코드를 실행.
실행 엔진은 기본적으로 1번의 방법을 통해 바이트 코드를 실행시키다가, 특정 바이트 코드가 자주 실행되면 해당 바이트 코드를 JIT Compiler를 통해 실행.
즉, 중복적으로 어떤 바이트 코드가 등장할 때, 인터프리터는 매 번 해당 바이트 코드를 해석하고 실행하지만, JIT 컴파일러가 동작하면 한 번에 바이트 코드를 해석하고 실행시킨다.
Last In First Out
JVM이 작동되면 이 영역은 자동 생성
이 영역안에 객체나 인스턴스 변수, 배열이 저장
Person person = new Person();
위의 예시에서 new Person()
이 실행되면 Heap 영역에 인스턴스가 생성되며, 스턴스가 생성된 위치의 주소값을 person
에게 할당해주는데, 이 person
은 Stack 영역에 선언된 변수다.
객체를 다룬다는 것은 Stack 영역에 저장되어 있는 참조 변수를 통해 Heap 영역에 존재하는 객체를 다룬다는 의미가 된다.
정리하자면, Heap 영역은 실제 객체의 값이 저장되는 공간이다.
메모리를 자동으로 관리하는 프로세스. 프로그램에서 더 이상 사용하지 않는 객체를 찾아 삭제하거나 제거해 메모리를 확보하는 것을 의미.
가비지 컬렉션 동작방식
Young 영역에서는 새롭게 생성된 객체가 할당되는 곳이고 여기에는 많은 객체가 생성되었다 사라지는 것을 반복한다.
이 영역에서 활동하는 가비지 컬렉터를 Minor GC라고 부른다.
Old 영역에서는 Young영역에서 상태를 유지하고 살아남은 객체들이 복사되는 곳으로 보통 Young 영역보다 크게 할당되고 크기가 큰 만큼 가비지는 적게 발생한다.
이 영역에서 활동하는 가비지 컬렉터를 Major GC라고 부른다.
가비지 컬렉션 실행 2가지 단계
Stop The World를 통해 모든 작업이 중단되면, 가비지 컬렉션이 모든 변수와 객체를 탐색해서 각각 어떤 객체를 참고하고 있는지 확인한다.
이후, 사용되고 있는 메모리를 식별해서(Mark) 사용되지 않는 메모리는 제거(Sweep)하는 과정을 진행한다.
참고: codestates