JUINTINATION
스레드와 멀티태스킹 본문
반응형
멀티태스킹이란?
하나의 응용프로그램이 여러 개의 작업(태스크)을 동시에 처리하는 것
스레드란?
프로세스 내에서 실행되는 흐름의 단위
자바에서는 JVM에 의해 스케줄링되는 실행 단위의 코드 블럭
멀티스레딩이란?
하나의 응용프로그램을 여러 개의 스레드로 구성
한 스레드가 대기하는 동안 다른 스레드 실행
프로그램 전체적으로 시간 지연을 줄일 수 있음
자바의 멀티태스킹은 멀티스레딩만 가능
JVM과 멀티스레드의 관계
- 하나의 JVM은 하나의 자바 응용프로그램만 실행
- 자바 응용프로그램이 시작될 때 JVM이 함께 실행됨
- 자바 응용프로그램이 종료되면 JVM도 함께 종료됨
- 하나의 응용프로그램은 하나 이상의 스레드로 구성 가능
스레드를 만드는 2가지 방법
- java.lang.Thread 클래스 사용
- java.lang.Runnable 인터페이스 사용
Thread 클래스 작성
- Thread 클래스 상속 및 새 클래스 작성
- run() 메소드 오버라이딩
- run() 메소드를 스레드 코드라고 부름
- run() 메소드에서 스레드 실행 시작
class ThreadEx extends Thread {
// 코드 작성
@Override
public void run() {
// run() 메소드 오버라이딩
}
}
- 스레드 객체 생성 및 시작
- start() 메소드 호출로 스레드 시작
ThreadEx th = new ThreadEx(); // 스레드 객체 생성
th.start(); // 스레드 시작
Thread를 상속받아 1초 단위의 타이머 만들기
import java.awt.*;
import javax.swing.*;
class TimerThread extends Thread {
private JLabel timerLabel;
public TimerThread(JLabel timerLabel) {
this.timerLabel = timerLabel;
}
@Override
public void run() {
int n = 0;
while (true) {
timerLabel.setText(Integer.toString(n));
n++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
return;
}
}
}
}
public class ThreadTimerEx extends JFrame {
public ThreadTimerEx() {
setTitle("Thread를 상속받은 타이머 스레드 예제");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container c = getContentPane();
c.setLayout(new FlowLayout());
JLabel timerLabel = new JLabel();
timerLabel.setFont(new Font("Gothic", Font.ITALIC, 80));
c.add(timerLabel);
TimerThread th = new TimerThread(timerLabel);
setSize(300,170);
setVisible(true);
th.start();
}
public static void main(String[] args) {
new ThreadTimerEx();
}
}
Runnable 인터페이스로 스레드 만들기
- Runnable 인터페이스를 구현하는 새 클래스 작성
- run() 메소드 구현
- run() 메소드를 스레드 코드라고 부름
- run() 메소드에서 스레드 실행 시작
class RunnableThreadEx implements Runnable {
// 코드 작성
@Override
public void run() {
// run() 메소드 구현
}
}
- 스레드 객체 생성 및 시작
- start() 메소드 호출로 스레드 시작
Thread th = new ThreadEx(new RunnableThreadEx()); // 스레드 객체 생성
th.start(); // 스레드 시작
Thread를 상속받아 1초 단위의 타이머 만들기
import java.awt.*;
import javax.swing.*;
class TimerRunnable implements Runnable {
private JLabel timerLabel;
public TimerRunnable(JLabel timerLabel) {
this.timerLabel = timerLabel;
}
@Override
public void run() {
int n = 0;
while (true) {
timerLabel.setText(Integer.toString(n));
n++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
return;
}
}
}
}
public class RunnableTimerEx extends JFrame {
public RunnableTimerEx() {
setTitle("Runnable을 구현한 타이머 스레드 예제");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container c = getContentPane();
c.setLayout(new FlowLayout());
JLabel timerLabel = new JLabel();
timerLabel.setFont(new Font("Gothic", Font.ITALIC, 80));
c.add(timerLabel);
TimerRunnable runnable = new TimerRunnable(timerLabel);
Thread th = new Thread(runnable);
setSize(250, 150);
setVisible(true);
th.start();
}
public static void main(String[] args) {
new RunnableTimerEx();
}
}
스레드 상태
- NEW
- 스레드가 생성되었지만 스레드가 아직 실행할 준비가 되지 않았음
- RUNNABLE
- 스레드가 현재 실행되고 있거나
- 실행 준비되어 스케쥴링을 기다리는 상태
- WAITING
- wait()를 호출한 상태
- 다른 스레드가 notify()나 notifyAll()을 불러주기를 기다리고 있는 상태
- 스레드 동기화를 위해 사용
- wait()를 호출한 상태
- TIMED_WAITING
- sleep(n)을 호출하여 n 밀리초 동안 잠을 자고 있는 상태
- BLOCK
- 스레드가 I/O 작업을 요청하면 JVM이 자동으로 BLOCK 상태로 만듦
- TERMINATED
- 스레드가 종료한 상태
- 스레드 상태는 JVM에 의해 기록 관리됨
- 스레드의 우선순위
- 최대값 = 10(MAX_PRIORITY)
- 최소값 = 1(MIN_PRIORITY)
- 보통값 = 5(NORMAL_PRIORITY)
- 스레드 우선순위는 응용프로그램에서 변경 가능
- void setPriority(int priority)
- int getPriority()
- main() 스레드의 우선순위 값은 초기에 5
- 스레드는 부모 스레드와 동일한 우선순위 값을 가지고 탄생
- JVM의 스케쥴링 정책
- 철저한 우선순위 기반
- 가장 높은 우선순위의 스레드가 우선적으로 스케쥴링
- 동일한 우선순위의 스레드는 돌아가면서 스케쥴링(라운드 로빈)
- 철저한 우선순위 기반
스레드 종료와 타 스레드 강제 종료
- 스스로 종료 : run() 메소드 리턴
- 타 스레드에서 강제 종료 ; interrupt() 메소드 사용
public static void main(String [] args) {
TimerThread th = new TimerThread();
th.start();
th.interrupt(); // TimerThread 강제 종료
}
타이머 스레드 강제 종료
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class TimerRunnable implements Runnable {
private JLabel timerLabel;
public TimerRunnable(JLabel timerLabel) {
this.timerLabel = timerLabel;
}
@Override
public void run() {
int n = 0;
while (true) {
timerLabel.setText(Integer.toString(n));
n++;
try {
Thread.sleep(1000); // 1초 동안 잠을 잔다.
} catch (InterruptedException e) {
return; // 예외가 발생하면 스레드 종료
}
}
}
}
public class ThreadInterruptEx extends JFrame {
private Thread th;
public ThreadInterruptEx() {
setTitle("ThreadInterruptEx 예제");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container c = getContentPane();
c.setLayout(new FlowLayout());
JLabel timerLabel = new JLabel();
timerLabel.setFont(new Font("Gothic", Font.ITALIC, 80));
TimerRunnable runnable = new TimerRunnable(timerLabel);
th = new Thread(runnable); // 스레드 생성
c.add(timerLabel);
// 버튼을 생성하고 Action 리스너 등록
JButton btn = new JButton("kill Timer");
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
th.interrupt(); // 타이머 스레드 강제 종료
JButton btn = (JButton) e.getSource();
btn.setEnabled(false); // 버튼 비활성화
}
});
c.add(btn);
setSize(300, 170);
setVisible(true);
th.start(); // 스레드 동작시킴
}
public static void main(String[] args) {
new ThreadInterruptEx();
}
}
스레드 동기화(Thread Synchronization)
- 멀티스레드 프로그램 작성시 주의점
- 다수의 스레드가 공유 데이터에 동시에 접근하는 경우
- 공유 데이터의 값에 예상치 못한 결과 발생 가능
- 다수의 스레드가 공유 데이터에 동시에 접근하는 경우
- 스레드 동기화
- 멀티스레드의 공유 데이터의 동시 접근 문제 해결책
- 공유 데이터를 접근하는 모든 스레드의 한 줄 세우기
- 한 스레드가 공유 데이터에 대한 작업을 끝낼 때까지다른 스레드가 대기 하도록 함
- 멀티스레드의 공유 데이터의 동시 접근 문제 해결책
- 스레드 동기화
- 공유 데이터에 동시에 접근하는 다수의 스레드가 공유 데이터를 배타적으로 접근하기 위해 상호 협력(coordination)하는 것
- 동기화의 핵심
- 스레드의 공유 데이터에 대한 배타적 독점 접근 보장
자바에서 스레드 동기화를 위한 방법
- 스레드의 공유 데이터에 대한 배타적 독점 접근 보장
- synchronized로 동기화 블록 지정
- wait()-notify() 메소드로 스레드 실행 순서 제어
- synchronized 키워드
- 한 스레드가 독점 실행해야 하는 부분(동기화 코드)을 표시하는 키워드
- 임계 영역(critical section) 표기 키워드
- 메소드 전체 혹은 코드 블록
- 한 스레드가 독점 실행해야 하는 부분(동기화 코드)을 표시하는 키워드
- synchronized 블록에 대한 컴파일러의 처리
- 먼저 실행한 스레드가 모니터 소유
- 모니터란 해당 객체를 독점적으로 사용할 수 있는 권한
- 모니터를 소유한 스레드가 모니터를 내놓을 때까지 다른 스레드 대기
- 먼저 실행한 스레드가 모니터 소유
- 동기화 객체
- 두 개 이상의 스레드 동기화에 사용되는 객체
- 동기화 메소드
- wait()
- 다른 스레드가 notify()를 불러줄 때까지 기다린다.
- notify()
- wait()를 호출하여 대기중인 스레드를 깨우고 RUNNABLE 상태로 만든다.
- 2개 이상의 스레드가 대기중이라도 오직 한 스레드만 깨운다.
- notifyAll()
- wait()를 호출하여 대기중인 모든 스레드를 깨우고모두 RUNNABLE 상태로 만든다.
- synchronized 블록 내에서만 사용되어야 함
- wait()
- wait(), notify(), notifyAll()은 Object의 메소드
- 모든 객체가 동기화 객체가 될 수 있다.
- Thread 객체도 동기화 객체로 사용될 수 있다.
내 생각
처음에 수업 시간에 스레드에 대해 배우면서 이게 무슨 말인가 싶었습니다. 특히 Synchronized 부분이 이해가 안 됐는데 교재에 공유 게시판에 2개의 스레드가 동시에 접근하여 값을 더해가며 출력하는 예제가 있었지만 JVM에 의해 자동으로 순서가 정해지기 때문에 차례대로 번갈아가며 출력되지 않았습니다. 그래서 예제 정답 코드를 수정해가며 이해를 시도해 봤고 다음은 50ms마다 A와 B가 번갈아가며 출력하게 되도록 수정하면서 어느 정도 이해할 수 있었던 것 같습니다.
public class SynchronizedEx {
public static void main(String[] args) {
SharedBoard board = new SharedBoard();
Thread th1 = new StudentThread("A", board);
Thread th2 = new StudentThread("B", board);
th1.start();
th2.start();
}
}
class SharedBoard {
private int sum = 0;
synchronized public void add() {
sum += 10;
System.out.println(Thread.currentThread().getName() + " : " + sum);
notifyAll();
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class StudentThread extends Thread {
private final SharedBoard board;
public StudentThread(String name, SharedBoard board) {
super(name);
this.board = board;
}
@Override
public void run() {
for (int i = 0; i < 3; i++) {
board.add();
try {
sleep(50);
} catch (InterruptedException e) {
return;
}
}
System.exit(0);
}
}
728x90
'자바프로그래밍및실습' 카테고리의 다른 글
JDBC 프로그래밍 (0) | 2023.01.08 |
---|---|
소켓 프로그래밍 (0) | 2023.01.06 |
제네릭과 컬렉션 (0) | 2022.12.22 |
Comments