자바 애플리케이션을 실행하면 가장 먼저 실행되는 메서드는 main
메서드이며, 메인 스레드가 main
메서드를 실행시켜줌. 메인 스레드는 main
메서드의 코드를 처음부터 끝까지 순차적으로 실행시키며, 코드의 끝을 만나거나 return
문을 만나면 실행을 종료함.
메인 스레드 외에 별도의 작업 스레드를 활용한다는 것: 작업 스레드가 수행할 코드를 작성하고, 작업 스레드를 생성하여 실행시키는 것
스레드가 수행할 코드도 클래스 내부에 작성해주어야 하며, run()
이라는 메서드 내에 스레드가 처리할 작업을 작성하도록 규정되어 있음. run()
메서드는 Runnable 인터페이스와 Thread 클래스에 정의되어져 있음.
작업 스레드를 생성하고 실행하는 방법
1. Runnable 인터페이스를 구현한 객체에서 run()
을 구현하여 스레드를 생성하고 실행하는 방법
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());
// 작업 스레드를 실행시켜, run() 내부의 코드를 처리하도록 함
thread1.start();
// 반복문 추가
for (int i = 0; i < 100; i++) {
System.out.print("@");
}
}
}
// Runnable 인터페이스를 구현하는 클래스
class ThreadTask1 implements Runnable {
// run() 메서드 바디에 스레드가 수행할 작업 내용 작성
public void run() {
for (int i = 0; i < 100; i++) {
System.out.print("#");
}
}
}
// 출력값 (매 실행 시마다 다를 수 있음)
@@@@@@@@@@@######@@@@@############################
@#########@@@@@@@@@@@@@@@@############@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@##@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@###########################################
Process finished with exit code 0
@
는 main
메서드의 반복문에서 출력한 문자. 즉 @
는 메인 스레드의 반복문 코드 실행에 의해 출력되었음#
는 run()
메서드의 반복문에서 출력한 문자. 즉 #
는 작업 스레드의 반복문 코드 실행에 의해 출력되었음@
와 #
는 섞여 있음. 즉 메인 스레드와 작업 스레드가 동시에 병렬로 실행되면서 각각 main
메서드와 run()
메서드의 코드를 실행시켰기 때문에 두 가지 문자가 섞여서 출력된 것.run()
을 구현하여 스레드를 생성하고 실행하는 방법public class ThreadExample2 {
public static void main(String[] args) {
// Thread 클래스를 상속받은 클래스를 인스턴스화하여 스레드를 생성
ThreadTask2 thread2 = new ThreadTask2();
// 작업 스레드를 실행시켜, run() 내부의 코드를 처리하도록 합니다.
thread2.start();
// 반복문 추가
for (int i = 0; i < 100; i++) {
System.out.print("@");
}
}
}
// Thread 클래스를 상속받는 클래스 작성
class ThreadTask2 extends Thread {
// run() 메서드 바디에 스레드가 수행할 작업 내용 작성
public void run() {
for (int i = 0; i < 100; i++) {
System.out.print("#");
}
}
}
Runnable 익명 구현 객체를 활용한 스레드 생성 및 실행
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("@");
}
}
}
Thread 익명 하위 객체를 활용한 스레드 생성 및 실행
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"이라는 이름을 가짐
스레드의 이름은 스레드의_참조값.getName()
으로 조회할 수 있음.
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());
}
}
// 출력값
/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=52285:/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 ThreadExample3
Get Thread Name
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());
}
}
// 출력값
/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=52282:/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 ThreadExample4
Set And Get Thread Name
thread4.getName() = Thread-0
thread4.getName() = Code States
Process finished with exit code 0
스레드의 이름을 조회하고 설정하는 위 두 메서드는 모두 Thread 클래스로부터 인스턴스화된 인스턴스의 메서드이므로, 호출할 때에 스레드 객체의 참조가 필요함.
만약 실행 중인 스레드의 주소값을 사용해야 하는 상황이 발생한다면 Thread 클래스의 정적 메서드인 currentThread()
를 사용.
public 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) {}
Thread.sleep(1000);
Thread.sleep()
은 반드시 try ~ catch
문의 try
블럭 내에 작성해주어야 함try { ... } catch ( ~ ) { ... }
try ... catch
문은 예외 처리에 사용되는 문법try
의 블록 내의 코드를 실행하다가 예외 또는 에러가 발생하면 catch
문의 블럭에 해당하는 내용을 실행하라는 의미synchronized
라는 키워드를 사용. 두 가지 방법으로 사용 가능withdraw()
가 호출되면 withdraw()
를 실행하는 스레드는 withdraw()
가 포함된 객체의 락을 얻으며 해당 스레드가 락을 반납하기 이전에 다른 스레드는 해당 메서드의 코드를 실행하지 못하기 됨class Account {
...
public synchronized boolean withdraw(int money) {
if (balance >= money) {
try { Thread.sleep(1000); } catch (Exception error) {}
balance -= money;
return true;
}
return false;
}
}
synchronized
키워드와 함께 소괄호(()
) 안에 해당 영역이 포함된 객체의 참조를 넣고, 중괄호({}
)로 블럭을 열어 블럭 내에 코드를 장성함. 임계 영역으로 설정한 블럭의 코드로 코드 실행 흐름이 진입할 때, 해당 코드를 실행하고 있는 스레드가 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;
}
}
}