자바의 Thread

mDev_97·2022년 1월 28일
0

Java

목록 보기
27/28

멀티태스킹(multi-tasking)

• 하나의 응용 프로그램이 여러 개의 작업(task)을 동시에 처리하는 것

스레드와 멀티 스레딩

스레드

• 사용자가 작성한 코드
• JVM(자바 가상 기계)에 의해 스케쥴링되어 실행되는 단위
• JVM에 의한 실행 단위

자바의 멀티태스킹

• 멀티스레딩만 가능
하나의 응용프로그램여러 개의 스레드로 구성 가능하다.

멀티스레딩 효과

• 한 스레드가 대기하는 동안 다른 스레드를 실행할 수 있다.
-> 프로그램 전체적으로 시간 지연을 줄일 수 있다.

자바 Thread

• JVM에 의해 스케쥴되는 실행 단위의 코드 블럭
• 스레드의 주기는 JVM에 의해 관리된다.

JVM과 멀티 스레드의 관계

• 하나의 JVM은 하나의 응용프로그램만 실행한다.
• 하나의 응용 프로그램은 하나 이상의 스레드로 구성할 수 있다.

※ 두 개의 응용프로그램을 동시에 실행하고자 하면 두 개의 JVM을 이용

스레드의 생성과 실행

실행을 위해 개발자가 하는 작업

• 스레드 코드 작성
• 스레드를 생성하고 JVM에게 스레드 코드 실행을 요청

스레드를 만들 때 주의사항

• run() 메소드 종료 시 스레드도 종료
• 한 번 종료한 스레드는 다시 시작시킬 수 없다.
• 한 스레드에서 다른 스레드를 가젱 종료시킬 수 있다.

스레드 생성 방법

1. Thread 클래스를 상속

• java.lang.Thread 클래스를 이용

// 스레드 클래스 작성
// Thread 클래스 상속
class ThreadEx extends Thread {
    ......
    @Override
    public void run() { // run() 메소드 오버라이딩
        ......
    } // 스레드 코드라고 부름
      // 스레드 실행 시작
    ......
}

• 스레드 객체 생성 및 스레드 시작

ThreadEx thread = new ThreadEx(); // 스레드 객체 생성
thread.start(); // start() 메소드 호출
// 스레드로 작동 시작
// JVM에 의해 스케쥴되기 시작한다.

Thread 클래스 상속 예제

• 타이머 예제

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();
    }
}

• 실행 결과

2. Runnable 인터페이스를 구현

• java.lang.Runnable 인터페이스를 이용

// Runnable 인터페이스 구현하여 클래스 작성
class ThreadEx implements Runnable {
    ......
    @Override
    public void run() { // run() 메소드 구현
        ......
    } // 스레드 코드, 스레드 실행 시작
    ......
}

• 스레드 객체 생성 및 스레드 시작

Thread thread = new Thread(new ThreadEx()); // 스레드 객체 생성
thread.start(); // 스레드 시작

Runnable 상속 스레드 예제

• 타이머 예제

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에 의해 기록 관리된다.

NEW

• 스레드가 생성되었지만 아직 실행할 준비 x

RUNNABLE

• 스레드가 실행 중 OR 실행 준비가 되어 스케쥴링을 대기하는 상태

WAITING

• wait() 메소드를 호출한 상태
• 스레드 동기화 시에 사용
• 다른 스레드가 notify(), notifyAll() 메소드를 호출하기를 기다리는 상태

TIMED_WAITING

• sleep() 메소드를 호출하여 대기 중인 상태

BLOCK

• 스레드가 입출력 작업을 요청하면 자바 가상 기계가 자동으로 BLOCK 상태로 만든다.

TERMINATED

• 스레드 종료된 상태

스레드 우선순위

• 스레드의 우선순위는 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 스레드

main()을 실행
• 응용프로그램을 실행할 때 JVM이 스레드 생성
-> main 스레드
• JVM이 main 스레드가 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를 이용한 종료

• 다른 스레드에서 한 스레드의 flag를 true로 만들면 스레드 종료

타이머 flag를 이용한 종료 예제

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();
    }
}

• 실행 결과

• 버튼 클릭 시

profile
안녕하세요. 백엔드, 클라우드, 인프라에 관심과 열정이 있는 김문성입니다. 😊

0개의 댓글

관련 채용 정보