스레드는 운영 체제가 작업을 스케줄링할 수 있는 가장 작은 실행 단위입니다. 프로세스 내부에 포함되어 있으며, 하나의 프로세스는 여러 개의 스레드를 가질 수 있습니다. 각 스레드는 독립적으로 다른 작업을 수행합니다.
1. Thread 클래스 상속
Thread 클래스를 상속하고 run 메서드를 재정의한 다음 start 메서드를 호출하여 스레드를 시작할 수 있습니다.
public class CustomThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("작업 중: " + i);
}
}
public static void main(String[] args) {
CustomThread thread = new CustomThread();
thread.start();
for (int i = 0; i < 50; i++) {
System.out.println("메인 작업: " + i);
}
}
}
2. Runnable 인터페이스 구현
Runnable 인터페이스를 구현하고 run 메서드를 재정의하면 단일 상속의 제약을 피할 수 있습니다. 이 방식은 더 일반적으로 사용됩니다.
public class Task implements Runnable {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("작업 진행: " + i);
}
}
public static void main(String[] args) {
Task task = new Task();
Thread worker = new Thread(task);
worker.start();
for (int i = 0; i < 100; i++) {
System.out.println("메인 스레드: " + i);
}
}
}
3. Callable 인터페이스 구현
Callable 인터페이스는 결과값을 반환할 수 있으며 예외 처리도 가능합니다. ExecutorService를 통해 관리할 수 있습니다.
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class AsyncTask implements Callable<Boolean> {
private String name;
public AsyncTask(String name) {
this.name = name;
}
@Override
public Boolean call() throws Exception {
Thread.sleep(100);
System.out.println(name + " 작업 완료");
return true;
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService service = Executors.newFixedThreadPool(3);
Future<Boolean> result1 = service.submit(new AsyncTask("A"));
Future<Boolean> result2 = service.submit(new AsyncTask("B"));
Future<Boolean> result3 = service.submit(new AsyncTask("C"));
System.out.println("결과1: " + result1.get());
System.out.println("결과2: " + result2.get());
System.out.println("결과3: " + result3.get());
service.shutdown();
}
}
추가 설명
정적 프록시 패턴 예제
프록시 패턴을 이용해 실제 객체와 대리 객체 사이에서 작업을 처리할 수 있습니다.
public class ProxyExample {
public static void main(String[] args) {
Event event = new ProxyHandler(new RealEvent());
event.perform();
// 람다 표현식으로 스레드 생성
new Thread(() -> System.out.println("새로운 스레드")).start();
}
}
interface Event {
void perform();
}
class RealEvent implements Event {
@Override
public void perform() {
System.out.println("실제 이벤트 실행");
}
}
class ProxyHandler implements Event {
private Event target;
public ProxyHandler(Event target) {
this.target = target;
}
@Override
public void perform() {
before();
target.perform();
after();
}
private void before() {
System.out.println("이전 작업 수행");
}
private void after() {
System.out.println("이후 작업 수행");
}
}
람다 표현식 사용
함수형 인터페이스를 사용하면 람다 표현식으로 간단히 코드를 작성할 수 있습니다.
public class LambdaTest {
public static void main(String[] args) {
Animal animal = (type1, type2) -> {
System.out.println(type1 + " 동물 움직임");
System.out.println(type2 + " 동물 움직임");
};
animal.move("고양이", "강아지");
}
}
interface Animal {
void move(String type1, String type2);
}