• 하나의 응용 프로그램이 여러 개의 작업(task)을 동시에 처리하는 것
• 사용자가 작성한 코드
• JVM(자바 가상 기계)에 의해 스케쥴링되어 실행되는 단위
• JVM에 의한 실행 단위
• 멀티스레딩만 가능
• 하나의 응용프로그램은 여러 개의 스레드로 구성 가능하다.
• 한 스레드가 대기하는 동안 다른 스레드를 실행할 수 있다.
-> 프로그램 전체적으로 시간 지연을 줄일 수 있다.
• JVM에 의해 스케쥴되는 실행 단위의 코드 블럭
• 스레드의 주기는 JVM에 의해 관리된다.
• 하나의 JVM은 하나의 응용프로그램만 실행한다.
• 하나의 응용 프로그램은 하나 이상의 스레드로 구성할 수 있다.
※ 두 개의 응용프로그램을 동시에 실행하고자 하면 두 개의 JVM을 이용
• 스레드 코드 작성
• 스레드를 생성하고 JVM에게 스레드 코드 실행을 요청
• run() 메소드 종료 시 스레드도 종료
• 한 번 종료한 스레드는 다시 시작시킬 수 없다.
• 한 스레드에서 다른 스레드를 가젱 종료시킬 수 있다.
• java.lang.Thread 클래스를 이용
// 스레드 클래스 작성
// Thread 클래스 상속
class ThreadEx extends Thread {
......
@Override
public void run() { // run() 메소드 오버라이딩
......
} // 스레드 코드라고 부름
// 스레드 실행 시작
......
}
• 스레드 객체 생성 및 스레드 시작
ThreadEx thread = new ThreadEx(); // 스레드 객체 생성
thread.start(); // start() 메소드 호출
// 스레드로 작동 시작
// JVM에 의해 스케쥴되기 시작한다.
• 타이머 예제
import java.awt.*;
import javax.swing.*;
class ThreadEx extends Thread {
private JLabel timer;
public ThreadEx(JLabel timer) {
this.timer = timer;
}
@Override
public void run() { // run() 메소드 오버라이딩
int n = 0;
while(true) {
timer.setText(Integer.toString(n)); // n을 timer 라벨에 setText
n++;
try {
Thread.sleep(1000); // 1초 대기
} catch(InterruptedException e) {
return; // 리턴하여 스레드를 종료
}
}
}
}
public class ThreadClassEx extends JFrame {
public ThreadClassEx() {
super("Thread 클래스 상속 예제");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container contentPane = getContentPane();
contentPane.setLayout(new FlowLayout());
JLabel timer = new JLabel();
timer.setFont(new Font("Times", Font.BOLD, 60));
contentPane.add(timer);
ThreadEx thread = new ThreadEx(timer); // 스레드 객체 생성
setSize(300, 200);
setVisible(true);
thread.start();
}
public static void main(String[] args) {
new ThreadClassEx();
}
}
• 실행 결과
• java.lang.Runnable 인터페이스를 이용
// Runnable 인터페이스 구현하여 클래스 작성
class ThreadEx implements Runnable {
......
@Override
public void run() { // run() 메소드 구현
......
} // 스레드 코드, 스레드 실행 시작
......
}
• 스레드 객체 생성 및 스레드 시작
Thread thread = new Thread(new ThreadEx()); // 스레드 객체 생성
thread.start(); // 스레드 시작
• 타이머 예제
import java.awt.*;
import javax.swing.*;
class ThreadEx implements Runnable {
private JLabel timer;
public ThreadEx(JLabel timer) {
this.timer = timer;
}
@Override
public void run() { // run() 메소드 오버라이딩
int n = 0;
while(true) {
timer.setText(Integer.toString(n)); // n을 timer에 setText
n++;
try {
Thread.sleep(1000); // 1초 동안 대기
} catch(InterruptedException e) {
return; // 리턴하여 스레드를 종료
}
}
}
}
public class RunnableThreadEx extends JFrame {
public RunnableTreadEx() {
super("Runnable 구현 Thread 예제");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container contentPane = getContentPane();
contentPane.setLayout(new FlowLayout());
JLabel timer = new JLabel();
timer.setFont(new Font("Times", Font.BOLD, 60));
contentPane.add(timer);
ThreadEx runnableThread = new ThreadEx(timer); // 인터페이스 클래스 객체
Thread thread = new Thread(runnableThread); // 스레드 객체 생성
setSize(300, 200);
setVisible(true);
thread.start(); // 스레드 실행
}
public static void main(String[] args) {
new RunnableThreadEx();
}
}
• 실행 결과
• JVM에 의해 기록 관리된다.
• 스레드가 생성되었지만 아직 실행할 준비 x
• 스레드가 실행 중 OR 실행 준비가 되어 스케쥴링을 대기하는 상태
• wait() 메소드를 호출한 상태
• 스레드 동기화 시에 사용
• 다른 스레드가 notify(), notifyAll() 메소드를 호출하기를 기다리는 상태
• sleep() 메소드를 호출하여 대기 중인 상태
• 스레드가 입출력 작업을 요청하면 자바 가상 기계가 자동으로 BLOCK 상태로 만든다.
• 스레드 종료된 상태
• 스레드의 우선순위는 1 ~ 10까지 가질 수 있다.
• 숫자가 높을수록 우선순위가 높다.
• static int MAX_PRIORITY : 최대 우선순위 (10)
• static int MIN_PRIORITY : 최소 우선순위 (1)
• static int NORMAL_PRIORITY: 기본 우선순위
※스레드 우선순위는 응용프로그램에서 변경 가능하다.
void setPriority(int priority)
int getPriority()
• main() 스레드의 초기 우선순위는 5
• 스레드는 부모 스레드와 동일한 우선순위를 가진다.
• main()을 실행
• 응용프로그램을 실행할 때 JVM이 스레드 생성
-> main 스레드
• JVM이 main 스레드가 main() 메소드를 실행하게 한다.
public class MainThreadEx {
public static void main(String[] args) {
long id = Thread.currentThread().getId(); // 현재 Thread의 ID 얻기
String name = Thread.currentThread().getName(); // 현재 Thread의 이름 얻기
int priority = Thread.currentThread().getPriority(); // 현재 Thread의 우선순위 값 얻ㄱ이
Thread.State state = Thread.currentThread().getState(); // 현재 Thread의 상태 값 얻기
System.out.println("현재 스레드 ID : " + id);
System.out.println("현재 스레드 이름 : " + name);
System.out.println("현재 스레드 우선순위 : " + priority);
System.out.println("현재 스레드 상태 값 : " + state);
}
}
• 실행 결과
현재 스레드 ID : 1
현재 스레드 이름 : main
현재 스레드 우선순위 : 5
현재 스레드 상태 값 : RUNNABLE
• run() 메소드 리턴
class ThreadEx extends Thread {
int n = 0;
@Override
public void run() { // run() 메소드 오버라이딩
while(true) {
System.out.print(n); // 1초마다 n 값 출력
n++
try {
sleep(1000); // 1초 동안 대기
} catch(InterruptedException e) {
return; // 리턴하여 종료
}
}
}
}
• 다른 스레드에서 강제 종료
• interrupt() 메소드 사용
public static void main(String[] args) {
ThreadEx thread = new ThreadEx(); // 스레드 객체 생성
thread.start(); // 스레드 실행
thread.interrupt(); // 스레드 강제 종료
}
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class ThreadEx extends Thread {
private JLabel timer;
public ThreadEx(JLabel timer) {
this.timer = timer;
}
@Override
public void run() { // run() 메소드 오버라이딩
int n = 0;
while(true) {
timer.setText(Integer.toString(n)); // timer에 n을 setText
n++;
try {
sleep(1000); // 1초 동안 대기
} catch(InterruptedException e) {
return; // 예외 발생 시 스레드를 종료
}
}
}
}
public class ThreadEndEx extends JFrame {
public ThreadEndEx() {
super("thread 종료 예제");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container contentPane = getContentPane();
contentPane.setLayout(new FlowLayout());
JLabel timer = new JLabel();
timer.setFont(new Font("Times", Font.BOLD, 60));
ThreadEx thread = new ThreadEx(timer); // 스레드 객체 생성
contentPane.add(timer);
// 스레드를 종료시킬 버튼 & 이벤트 생성
JButton endBtn = new JButton("End Timer");
endBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
thread.interrupt(); // 스레드 종료
JButton btn = (JButton)e.getSource();
btn.setEnabled(false); // 버튼은 더이상 사용하지 못하도록 비활성화
}
});
contentPane.add(endBtn);
setSize(300, 200);
setVisible(true);
thread.start(); // 스레드 실행
}
public static void main(String[] args) {
new ThreadEndEx();
}
}
• 실행 결과
• 버튼 클릭 시
• 다른 스레드에서 한 스레드의 flag를 true로 만들면 스레드 종료
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class ThreadEx extends Thread {
private JLabel timer;
private boolean flag = false;
public void finish() {
flag = true;
}
public ThreadEx(JLabel timer) {
this.timer = timer;
}
@Override
public void run() { // run() 메소드 오버라이딩
int n = 0;
while(true) {
timer.setText(Integer.toString(n)); // timer에 n을 setText
n++;
try {
sleep(1000); // 1초 동안 대기
if(flag == true) { // flag가 true이면 리턴하여 종료
return;
}
} catch(InterruptedException e) {
return; // 예외 발생 시 스레드를 종료
}
}
}
}
public class ThreadFlagEx extends JFrame {
public ThreadFlagEx() {
super("flag를 이용한 thread 종료 예제");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container contentPane = getContentPane();
contentPane.setLayout(new FlowLayout());
JLabel timer = new JLabel();
timer.setFont(new Font("Times", Font.BOLD, 60));
ThreadEx thread = new ThreadEx(timer); // 스레드 객체 생성
contentPane.add(timer);
// 스레드를 종료시킬 버튼 & 이벤트 생성
JButton endBtn = new JButton("End Timer");
endBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
thread.finish(); // 스레드 종료
JButton btn = (JButton)e.getSource();
btn.setEnabled(false); // 버튼은 더이상 사용하지 못하도록 비활성화
}
});
contentPane.add(endBtn);
setSize(300, 200);
setVisible(true);
thread.start(); // 스레드 실행
}
public static void main(String[] args) {
new ThreadFlagEx();
}
}
• 실행 결과
• 버튼 클릭 시