12.1 멀티태스킹
멀티태스킹이란?
: 다수의 작업을 동시에 처리하는 것
멀티태스킹 프로그램 사례
: 하나의 프로그램이 하나의 작업(태스크)만 하는 경우가 대부분이지만, 하나의 프로그램이 여러 작업(태스크)을 동시에 실행하는 경우가 있다.
스레드와 운영체제
- 스레드(thread): 운영체제에 의해 관리되는 하나의 태스크
- 멀티스레딩(multi-threading): 다수의 스레드를 동시에 실행시키도록 응용프로그램을 작성하는 기법
멀티태스킹과 멀티스레딩
- 멀티프로세싱(multi-processing): 하나의 응용프로그램을 여러 개의 프로세스(process)로 구성하여 각 프로세스가 하나의 작업(태스크)을 처리하도록 하는 기법
자바 스레드와 JVM
- 자바 스레드(java thread)
- 자바 가상기계(JVM: Java Virtual Machine): 운영체제 역할
12.2 자바 스레드 만들기
Thread 클래스로 스레드 만들기
- Thread 클래스를 상속받아 run() 오버라이딩
- 스레드 객체 생성
- 스레드 시작: start() 메소드 호출
/*
* 예제12-1: Thread를 상속받아 1초 단위로 출력하는 타이머 스레드 만들기
*/
import javax.swing.*;
import java.awt.*;
class TimerThread extends Thread{
private JLabel timerLabel; // 타이머 값이 출력되는 레이블
public TimerThread(JLabel timerLabel) {
this.timerLabel = timerLabel; // 타이머 카운트를 출력할 레이블
}
// 스레드 코드. run()이 종료하면 스레드 종료
@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 TimerThreadEx extends JFrame {
public TimerThreadEx() {
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(400,400);
setVisible(true);
th.start(); // 타이머 스레드의 실행을 시작하게 한다.
}
public static void main(String[] args) {
new TimerThreadEx();
}
}
Runnable 인터페이스로 스레드 만들기
- 스레드 클래스 선언: Runnable 인터페이스 구현
- 스레드 객체 생성
- 스레드 시작: start() 메소드 호출
/*
* Runnable 인터페이스를 이용하여 1초 단위로 출력하는 타이머 스레드
*/
import javax.swing.*;
import java.awt.*;
class TimerRunnable implements Runnable{
private JLabel timerLabel; // 타이머 값이 출력된 레이블
public TimerRunnable(JLabel timerLabel) {
this.timerLabel = timerLabel; // 초 카운트를 출력할 레이블
}
// 스레드 코드. run()이 종료하면 스레드 종료
@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 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);
// 타이머 스레드로 활용할 Runnable 객체 생성. 타이머 값을 출력할 레이블을 생성자에 전달
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();
}
}
main 스레드
: JVM은 자바 응용프로그램을 실행하기 직전, 스레드를 하나 생성하고, 이 스레드로 하여금 main() 메소드를 실행하도록 한다. 이 스레드가 바로 메인 스레드(main 스레드)이고, 실행 시작 주소는 main() 메소드의 첫 코드가 된다.
/*
* main 스레드 확인과 스레드 정보를 알아내는 코드
*/
public class ThreadMainEx {
public static void main(String[] args) {
long id = Thread.currentThread().getId(); // 스레드 ID 얻기
String name = Thread.currentThread().getName(); // 스레드 이름 얻기
int priority = Thread.currentThread().getPriority(); // 스레드 우선순위 값 얻기
Thread.State s = Thread.currentThread().getState(); // 스레드 상태 값 얻기
System.out.println("현재 스레드 이름 = " + name);
System.out.println("현재 스레드 ID = " + id);
System.out.println("현재 스레드 우선순위 값 = " + priority);
System.out.println("현재 스레드 상태 = " + s);
}
}
12.3 스레드 종료
스스로 종료
강제 종료
/*
* 예제 12-4: 진동하는 스레드와 스레드의 강제 종료
*/
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
public class VibratingFrame extends JFrame implements Runnable {
private Thread th; // 진동하는 스레드
public VibratingFrame() {
setTitle("진동하는 프레임 만들기");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(200,200);
setLocation(300,300);
setVisible(true);
getContentPane().addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
if(!th.isAlive()) // 이미 스레드가 종료했다면 그냥 리턴
return;
th.interrupt(); // 진동 스레드에게 InterruptedException을 보내 강제 종료
}
});
th = new Thread(this); // 진동 스레드 객체 생성
th.start(); // 진동 시작
}
@Override
public void run() { // 프레임의 진동을 일으키기 위해 20ms마다 프레임의 위치를 랜덤하게 이동
Random r = new Random(); // 진동할 위치를 랜덤하게 발생시킬 랜덤 객체 생성
while(true) {
try {
Thread.sleep(20); // 20ms 잠자기
}
catch(InterruptedException e) {
return; // 리턴하면 스레드 종료
}
int x = getX() + r.nextInt()%5; // 새 위치 x
int y = getY() + r.nextInt()%5; // 새 위치 y
setLocation(x,y);
}
}
public static void main(String[] args) {
new VibratingFrame();
}
}
12.4 스레드 동기화
스레드 동기화의 필요성
: 멀티스레드 프로그램이 실행될 때, 다수의 스레드가 공유 데이터를 동시에 접근하는 경우가 발생한다. 특히 다수의 스레드가 동시에 공유 데이터 값을 변경시키는 경우, 공유 데이터 값이 정상적으로 변경되지 않게 된다.
- 스레드 동기화(thread synchronization): 공유 데이터를 동시 접근하는 여러 스레드에 의해 공유데이터 값이 비정상적으로 유지되지 않도록 스레드의 실행을 제어하는 기술
자바 스레드 동기화를 위한 synchronized 블록
synchronized 활용 사례
/*
* 예제12-5: 두 스레드가 공유 프린터 객체를 통해 동시에 출력하는 경우, synchronized 블록 지정
*/
public class SynchronizedEx {
public static void main(String[] args) {
SharedPrinter p = new SharedPrinter(); // 공유데이터 생성
String [] engText = {"Wise men say, ",
"only fools rush in",
"But I can't help, ",
"falling in love with you",
"Shall I stay? ",
"Would it be a sin?",
"If I can't help, ",
"falling in love with you"
};
String [] korText = {"동해물과 백두산이 마르고 닳도록, ",
"하느님이 보우하사 우리나라 만세",
"무궁화 삼천리 화려강산, ",
"대한 사람 대한으로 길이 보전하세",
"남산 위에 저 소나무, 철갑을 두른 듯",
"바람서리 불편함은 우리 기상일세.",
"무궁화 삼천리 화려강산, ",
"대한 사람 대한으로 길이 보전하세"
};
// 스레드 생성시 공유 프린터의 주소를 알려준다. 두 스레드는 공유 프린터 p에 동시에 접근한다.
Thread th1 = new WorkerThread(p, engText); // 영문 출력 스레드
Thread th2 = new WorkerThread(p, korText); // 국문 출력 스레드
// 두 스레드를 실행시킨다.
th1.start();
th2.start();
}
}
class SharedPrinter{ // 두 WorkerThread 스레드에 의해 동시 접근되는 공유 프린터
// synchronized를 생략하면 한글과 영어가 한 줄에 섞여 출력되는 경우가 발생한다.
synchronized void print(String text) {
for(int i=0; i<text.length(); i++)
System.out.print(text.charAt(i));
System.out.println();
}
}
class WorkerThread extends Thread{ // 스레드 클래스
private SharedPrinter p; // 공유프린터 주소
private String [] text;
public WorkerThread(SharedPrinter p, String [] text) { // 공유 프린터 주소와 텍스트 전달받음
this.p = p;
this.text = text;
}
@Override
public void run() {
for(int i=0; i<text.length; i++) // 한 줄씩 출력
p.print(text[i]); // 공유 프린터에 출력
}
}
wait()-notify()를 이용한 스레드 동기화
/*
* 예제12-6: wait(), notify()를 이용하여 키 입력으로 바 채우기
*/
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class MyLabel extends JLabel {
private int barSize = 0;
private int maxBarSize;
public MyLabel(int maxBarSize) {
this.maxBarSize = maxBarSize;
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.MAGENTA);
int width = (int) (((double) (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;
}
@Override
public void run() {
while (true) {
try {
sleep(100);
bar.consume();
} catch (InterruptedException e) {
return;
}
}
}
}
public class TabAndThreadEx extends JFrame {
private MyLabel bar = new MyLabel(100);
public TabAndThreadEx(String title) {
super(title);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container c = getContentPane();
c.setLayout(null);
bar.setBackground(Color.ORANGE);
bar.setOpaque(true);
bar.setLocation(20, 50);
bar.setSize(300, 20);
c.add(bar);
c.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
bar.fill();
}
});
setSize(350, 200);
setVisible(true);
c.setFocusable(true);
c.requestFocus();
ConsumerThread th = new ConsumerThread(bar);
th.start();
}
public static void main(String[] args) {
new TabAndThreadEx("아무키나 빨리 눌러 바 채우기");
}
}
'Programming > Java프로그래밍및실습' 카테고리의 다른 글
[자프실] 11. 그래픽 (0) | 2024.01.15 |
---|---|
[자프실] 10. 스윙 컴포넌트 활용 (1) | 2024.01.13 |
[자프실] 9. 자바의 이벤트 처리 (1) | 2024.01.11 |
[자프실] 8. 자바 GUI 스윙 기초 (1) | 2024.01.10 |
[자프실] 7. 컬렉션과 제네릭 (0) | 2024.01.07 |