객체지향 13장은 멀티스레드 프로그래밍을 다룹니다. 자바에서 스레드를 생성하고 관리하는 방법, 스레드의 동작 원리, 그리고 멀티스레드의 활용 사례들을 중심으로 설명하고 있습니다.
자바 멀티스레드 프로그래밍 – 스레드 관리와 동기화
1. 멀티스레딩 개념
멀티태스킹은 하나의 프로그램이 여러 작업(태스크)을 동시에 처리하는 것을 말합니다. 자바에서는 멀티태스킹을 멀티스레딩을 통해 구현할 수 있습니다. 멀티스레딩은 하나의 프로그램이 여러 개의 스레드를 실행해 동시에 여러 작업을 처리할 수 있게 합니다.
2. 스레드 생성 방법
자바에서 스레드를 생성하는 방법은 두 가지가 있습니다:
- hread 클래스 상속
- Runnable 인터페이스 구현
Thread 클래스 상속
Thread 클래스를 상속받아 run()
메소드를 오버라이딩하여 스레드의 동작을 정의할 수 있습니다. start()
메소드를 호출하여 스레드를 실행합니다.
class TimerThread extends Thread {
public void run() {
int n = 0;
while (true) {
System.out.println(n);
n++;
try {
Thread.sleep(1000); // 1초 대기
} catch (InterruptedException e) {
return;
}
}
}
}
public class ThreadExample {
public static void main(String[] args) {
TimerThread th = new TimerThread();
th.start(); // 스레드 실행
}
}
2. Runnable 인터페이스 구현
Runnable 인터페이스를 구현한 후, Thread
객체를 생성할 때 해당 Runnable 객체를 넘겨주는 방법입니다.
class TimerRunnable implements Runnable {
public void run() {
int n = 0;
while (true) {
System.out.println(n);
n++;
try {
Thread.sleep(1000); // 1초 대기
} catch (InterruptedException e) {
return;
}
}
}
}
public class RunnableExample {
public static void main(String[] args) {
Thread th = new Thread(new TimerRunnable());
th.start(); // 스레드 실행
}
}
3. 스레드 동작과 상태
스레드는 생명 주기를 가지며, 다양한 상태를 거칩니다:
- NEW: 스레드가 생성되었지만 아직 실행되지 않은 상태
- RUNNABLE: 스레드가 실행 중이거나 실행 대기 중인 상태
- WAITING: 스레드가 다른 스레드의 동작을 기다리는 상태
- TIMED WAITING: 일정 시간 동안 대기하는 상태
- BLOCKED: I/O 작업 등으로 인해 대기 중인 상태
- TERMINATED: 스레드가 종료된 상태
4. 스레드 동기화
멀티스레드 환경에서는 여러 스레드가 공유 자원에 접근할 때 동기화가 필요합니다. 자바는 synchronized
키워드를 사용해 한 번에 한 스레드만 공유 자원에 접근하도록 할 수 있습니다.
예제: 동기화 적용
class SharedBoard {
private int sum = 0;
synchronized public void add() {
int n = sum;
n += 10;
sum = n;
System.out.println(Thread.currentThread().getName() + " : " + sum);
}
public int getSum() {
return sum;
}
}
class StudentThread extends Thread {
private SharedBoard board;
public StudentThread(String name, SharedBoard board) {
super(name);
this.board = board;
}
public void run() {
for (int i = 0; i < 10; i++) {
board.add();
}
}
}
public class SynchronizedExample {
public static void main(String[] args) {
SharedBoard board = new SharedBoard();
Thread th1 = new StudentThread("학생1", board);
Thread th2 = new StudentThread("학생2", board);
th1.start();
th2.start();
}
}
이 예제에서는 두 스레드가 SharedBoard
객체의 add()
메소드를 동시에 호출할 수 없도록 synchronized
키워드를 사용하여 동기화를 구현했습니다.
5. 스레드 강제 종료와 interrupt()
스레드는 interrupt()
메소드를 사용해 다른 스레드를 강제 종료시킬 수 있습니다. InterruptedException
을 처리하여 스레드를 안전하게 종료하는 방법도 있습니다.
class TimerThread extends Thread {
public void run() {
int n = 0;
while (true) {
System.out.println(n);
n++;
try {
Thread.sleep(1000); // 1초 대기
} catch (InterruptedException e) {
return; // 예외 발생 시 스레드 종료
}
}
}
}
public class InterruptExample {
public static void main(String[] args) {
TimerThread th = new TimerThread();
th.start();
try {
Thread.sleep(5000); // 5초 대기
} catch (InterruptedException e) {}
th.interrupt(); // 스레드 강제 종료
}
}
6. wait(), notify(), notifyAll() 메소드
자바에서 스레드 간의 협력을 위해 wait()
, notify()
, notifyAll()
메소드를 사용하여 스레드를 일시적으로 대기시키고, 다른 스레드가 이를 깨울 수 있습니다. 이러한 메소드들은 반드시 synchronized
블록 내에서 사용되어야 합니다.
wait() 메소드
wait()
메소드는 현재 스레드를 일시 정지 상태로 만듭니다. 스레드는notify()
또는notifyAll()
메소드가 호출될 때까지 대기 상태에 머뭅니다.- 이 메소드는 주로 공유 자원을 다른 스레드가 사용할 때 대기하는 스레드에게 사용됩니다.
notify() 메소드
notify()
메소드는wait()
상태에서 대기 중인 스레드 중 하나를 깨워서 RUNNABLE 상태로 전환시킵니다.notify()
는 한 번에 하나의 스레드만 깨울 수 있습니다.
notifyAll() 메소드
notifyAll()
은 대기 중인 모든 스레드를 깨웁니다. 이 메소드를 사용하면 여러 스레드가wait()
상태에서 동시에 RUNNABLE 상태로 전환됩니다.
7. wait(), notify(), notifyAll() 메소드 예제 코드
다음은 wait()
, notify()
, notifyAll()
메소드를 사용하여 스레드 간 협력을 구현한 예제입니다. 이 예제에서는 두 스레드가 바를 채우고 소비하는 협력 작업을 수행합니다.
import javax.swing.*;
import java.awt.*;
class MyLabel extends JLabel {
private int barSize = 0; // 바 크기
private int maxBarSize;
public MyLabel(int maxBarSize) {
this.maxBarSize = maxBarSize;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.MAGENTA);
int width = (int)(((double)(this.getWidth())) / maxBarSize * barSize);
if (width == 0) return;
g.fillRect(0, 0, width, this.getHeight());
}
// 바를 채우는 메소드
synchronized void fill() {
if (barSize == maxBarSize) {
try {
wait(); // 바가 꽉 찼으면 대기
} catch (InterruptedException e) {
return;
}
}
barSize++; // 바를 채움
repaint();
notify(); // 대기 중인 스레드 깨움
}
// 바를 소비하는 메소드
synchronized void consume() {
if (barSize == 0) {
try {
wait(); // 바가 비었으면 대기
} catch (InterruptedException e) {
return;
}
}
barSize--; // 바를 소비함
repaint();
notify(); // 대기 중인 스레드 깨움
}
}
class ConsumerThread extends Thread {
private MyLabel bar;
public ConsumerThread(MyLabel bar) {
this.bar = bar;
}
public void run() {
while (true) {
try {
sleep(200); // 0.2초 대기
bar.consume(); // 바 소비
} catch (InterruptedException e) {
return;
}
}
}
}
public class WaitNotifyExample extends JFrame {
private MyLabel bar = new MyLabel(100); // 최대 100 크기의 바
public WaitNotifyExample(String title) {
super(title);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container c = getContentPane();
c.setLayout(null);
bar.setBackground(Color.ORANGE);
bar.setOpaque(true);
bar.setSize(300, 20);
bar.setLocation(20, 50);
c.add(bar);
// 키 입력으로 바 채우기
c.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
bar.fill();
}
});
setSize(350, 200);
setVisible(true);
c.requestFocus(); // 포커스 요청
ConsumerThread th = new ConsumerThread(bar); // 소비 스레드 실행
th.start();
}
public static void main(String[] args) {
new WaitNotifyExample("바 채우기 예제");
}
}
8. 코드 설명
- MyLabel 클래스
MyLabel
은 바를 그리기 위한 레이블입니다.fill()
메소드를 통해 바를 채우고,consume()
메소드를 통해 바를 소비합니다.synchronized
블록을 사용하여 한 번에 하나의 스레드만fill()
또는consume()
작업을 수행할 수 있도록 동기화합니다.wait()
메소드는 바가 꽉 차면 대기하고,notify()
메소드는 바가 채워지거나 소비된 후 다른 스레드를 깨웁니다.
- ConsumerThread 클래스
ConsumerThread
는 바를 소비하는 스레드입니다. 0.2초마다consume()
메소드를 호출하여 바를 하나씩 소비합니다.- 바가 비어있을 경우,
wait()
상태에서 대기하다가fill()
메소드가 호출되면 깨워져 다시 바를 소비합니다.
- WaitNotifyExample 클래스
WaitNotifyExample
은 JFrame을 상속받아 GUI를 구성하고, 키보드를 눌렀을 때fill()
메소드를 호출하여 바를 채웁니다.ConsumerThread
를 실행시켜 바를 계속해서 소비하도록 만듭니다.
마무리
이 코드에서는 wait()
, notify()
, notifyAll()
메소드를 통해 두 스레드 간의 협력을 구현했습니다. wait()
는 스레드가 조건이 충족될 때까지 대기 상태로 만들며, notify()
는 한 스레드를 깨우고, notifyAll()
은 모든 대기 중인 스레드를 깨웁니다. 이 메소드는 스레드 간의 효율적인 협력을 위해 필수적인 개념입니다.
'Java' 카테고리의 다른 글
명품자바 프로그래밍의 기초: 15장 (0) | 2024.09.18 |
---|---|
명품자바 프로그래밍의 기초: 14장 (0) | 2024.09.18 |
명품자바 프로그래밍의 기초: 12장 (0) | 2024.09.15 |
명품자바 프로그래밍의 기초: 11장 (0) | 2024.09.13 |
명품자바 프로그래밍의 기초: 10장 (0) | 2024.09.02 |