Process와 Thread
Process
: 실행 중인 프로그램
Multi Process
: 여러 개의 Thread를 실행하여 작동하는 프로그램
ex) 채팅
Multi Tasking
: 두 개 이상의 프로세스를 실행하여 일을 처리하는 것.
Thread
: 프로세스(process) 내에서 실제로 작업을 수행하는 주체
스레드는 비가역적이므로 종료한 스레드를 다시 불러와 실행할 수 없다. 똑같은 작업을 하고 싶을 시 새로운 스레드를 만들어 작동시켜야 한다.
싱글 스레드 프로그램 예시
package kr.or.ddit.basic;
public class T01ThreadTest {
public static void main(String[] args) {
// 싱글 스레드 프로그램
for(int i=1; i<=200; i++) {
System.out.print("*");
}
System.out.println();
for (int i = 0; i <= 200; i++) {
System.out.print("$");
}
}
}
멀티 스레드 프로그램 예시
- 스레드 생성 방법
1. Thread클래스를 상속한 클래스의 인스턴스를 생성 > 해당 인스턴스의 start()메서드 호출.
2. Runnable인터페이스를 구현한 클래스의 인스턴스를 생성 > Thread객체의 인스턴스 생성 시 생성자의 파라미터값으로 넣어줌 > 생성된 Thread객체의 start() 메서드 호출.
3. 익명클래스를 이용하는 방법
Runnable인터페이스를 구현한 익명 클래스를 Thread객체 생성 시 생성자의 파라미터값으로 넣어줌 > 생성된 Thread객체의 start()메서드 호출.
- 방법 1 이용
두 개의 스레드를 사용
Thread 메소드에서 작동할 것들은 run() 에 넣어놔야함
package kr.or.ddit.basic;
public class T02ThreadTest {
public static void main(String[] args) {
// 멀티 스레드 프로그램
// 스레드 생성하기
// 방법1 : Thread클래스를 상속한 클래스의 인스턴스를 생성한 후 해당 인스턴스의 start()메서드를 호출한다.
Thread th1 = new MyThread1();
th1.start();
}
}
class MyThread1 extends Thread {
@Override
public void run() {
for(int i=1; i<=200; i++) {
System.out.print("*");
try {
// Thread.sleep(시간) => 주어진 시간동안 작업을 잠시 멈춤.
// 시간은 밀리세컨드 단위 사용 (1000 밀리세컨드 = 1초).
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
- 방법 2 이용
package kr.or.ddit.basic;
public class T02ThreadTest {
public static void main(String[] args) {
// 멀티 스레드 프로그램
// 스레드 생성하기
// 방법1 : Thread클래스를 상속한 클래스의 인스턴스를 생성한 후 해당 인스턴스의 start()메서드를 호출한다.
Thread th1 = new MyThread1();
th1.start();
// 방법2 : Runnable인터페이스를 구현한 클래스의 인스턴스를 생성한 후 이 인스턴스를 Thread객체의 인스턴스를
// 생성할 때 생성자의 파라미터값으로 넣어준다. 이때 생성된 Thread객체의 start() 메서드를 호출한다.
Runnable r = new MyThread2();
Thread th2 = new Thread(r);
th2.start();
}
}
class MyThread1 extends Thread {
@Override
public void run() {
for(int i=1; i<=200; i++) {
System.out.print("*");
try {
// Thread.sleep(시간) => 주어진 시간동안 작업을 잠시 멈춤.
// 시간은 밀리세컨드 단위 사용 (1000 밀리세컨드 = 1초).
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class MyThread2 implements Runnable {
@Override
public void run() {
for(int i=1; i<=200; i++) {
System.out.print("$");
try {
// Thread.sleep(시간) => 주어진 시간동안 작업을 잠시 멈춤.
// 시간은 밀리세컨드 단위 사용 (1000 밀리세컨드 = 1초).
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
- 방법 3 이용
package kr.or.ddit.basic;
public class T02ThreadTest {
public static void main(String[] args) {
// 멀티 스레드 프로그램
// 스레드 생성하기
// 방법1 : Thread클래스를 상속한 클래스의 인스턴스를 생성한 후 해당 인스턴스의 start()메서드를 호출한다.
Thread th1 = new MyThread1();
th1.start();
// 방법2 : Runnable인터페이스를 구현한 클래스의 인스턴스를 생성한 후 이 인스턴스를 Thread객체의 인스턴스를
// 생성할 때 생성자의 파라미터값으로 넣어준다. 이때 생성된 Thread객체의 start() 메서드를 호출한다.
Runnable r = new MyThread2();
Thread th2 = new Thread(r);
th2.start();
// 방법3 : 익명클래스를 이용하는 방법
// Runnable인터페이스를 구현한 익명 클래스를 Thread객체 생성 시 생성자의 파라미터값으로 넣어준다.
// 이 때 생성된 Thread객체의 start()메서드를 호출한다.
Thread th3 = new Thread(new Runnable() {
@Override
public void run() {
for(int i=1; i<=200; i++) {
System.out.print("@");
try {
// Thread.sleep(시간) => 주어진 시간동안 작업을 잠시 멈춤.
// 시간은 밀리세컨드 단위 사용 (1000 밀리세컨드 = 1초).
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
th3.start();
}
}
class MyThread1 extends Thread {
@Override
public void run() {
for(int i=1; i<=200; i++) {
System.out.print("*");
try {
// Thread.sleep(시간) => 주어진 시간동안 작업을 잠시 멈춤.
// 시간은 밀리세컨드 단위 사용 (1000 밀리세컨드 = 1초).
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class MyThread2 implements Runnable {
@Override
public void run() {
for(int i=1; i<=200; i++) {
System.out.print("$");
try {
// Thread.sleep(시간) => 주어진 시간동안 작업을 잠시 멈춤.
// 시간은 밀리세컨드 단위 사용 (1000 밀리세컨드 = 1초).
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
- 방법 2와 방법 3의 차이
public class Test {
public static void main(String[] args) {
// extends Thread 를 사용할 때
스레드이름1 th1 = new 스레드이름1();
// implements Runnable 를 사용할 때
Thread th2 = new Thread(new 스레드이름2());
}
class 스레드이름1 extends Thread {
public 스레드이름1() {
}
}
class 스레드이름2 implements Runnable {
public 스레드이름2() {
}
}
- 스레드 사용법
extends Thread 또는 implements Runnable 하여 작동하여야 한다.
사용법 | 뜻 |
start() | 스레드 작업 시작 |
run() | 스레드 작업 진행 (main 메소드와 같은 기능으로 생각하면 됨) |
Thread.sleep() | 특정 시간동안 작업을 잠시 멈춤 |
join() | 작업중인 스레드가 종료될 때까지 기다림 |
System.exit(0); | 프로그램 종료 |
- 스레드 예시
- System.currentTimeMillis() 이용 => 처리시간
package kr.or.ddit.basic;
public class T03ThreadTest {
public static void main(String[] args) {
// 스레드의 수행시간 체크하기
Thread th = new Thread(new MyRunner());
// UTC(Universal Time Coordinated 협정 세계 표준 시)를 사용하여
// 1970년 1월 1일 0시 0분 0초를 기준으로 경과한 시간을 밀리세컨드(1/1000초) 단위로 나타낸다.
long startTime = System.currentTimeMillis();
th.start(); // 스레드 작업 시작
try {
th.join();
// 현재 실행중인 스레드에서 작업중인 스레드(지금은 th스레드)가 종료될 때까지 기다린다.
} catch (InterruptedException e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.println("경과 시간 : " + (endTime - startTime) + " ms");
}
}
// 1 ~ 1000000000 까지의 합계를 구하기 위한 클래스
class MyRunner implements Runnable {
@Override
public void run() {
long sum = 0;
for (int i = 0; i <= 1000000000; i++) {
sum += i;
}
System.out.println("합계 : " + sum);
}
}
=> 단일 스레드보다 멀티 스레드로 할 시 더 빠르게 작업할 수 있음
package kr.or.ddit.basic;
public class T04ThreadTest {
/*
* 1 ~ 20억까지의 합계를 구하는 데 걸린 시간을 확인해보기
*
* 전체 합계를 구하는 작업을 단일 스레드로 처리했을 때와 여러개의 스레드로 분할해서
* 작업할 때의 시간을 확인해보자.
*/
public static void main(String[] args) {
// 단일 스레드로 처리할 때...
SumThread sm = new SumThread(1, 2000000000);
long startTime = System.currentTimeMillis();
sm.start();
try {
sm.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.println("단독으로 처리 할 때의 처리시간(ms) : " + (endTime - startTime));
System.out.println("\n\n");
// 여러 스레드가 나누어 작업할 때
SumThread[] sumThs = new SumThread[] {
new SumThread( 1L, 500000000L),
new SumThread( 500000000L, 1000000000L),
new SumThread( 1000000000L, 1500000000L),
new SumThread( 1500000000L, 2000000000L)
};
startTime = System.currentTimeMillis();
for (Thread th : sumThs) {
th.start();
}
for (Thread th : sumThs) {
try {
th.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
endTime = System.currentTimeMillis();
System.out.println("여러 스레드로 나누어 처리 했을 때의 처리시간(ms) : "
+ (endTime - startTime));
}
}
class SumThread extends Thread {
private long min, max;
public SumThread(long min, long max) {
this.min = min;
this.max = max;
}
@Override
public void run() {
long sum = 0;
for (long i = min; i < max; i++) {
sum += i;
}
System.out.println(min + " ~ " + max + " 까지의 합 : " + sum);
}
}
- JOptionPane.showInputDialog 이용 => 입력 받기
package kr.or.ddit.basic;
import javax.swing.JOptionPane;
/**
* 단일스레드에서 사용자 입력 처리
*/
public class T05ThreadTest {
public static void main(String[] args) {
String input = JOptionPane.showInputDialog("아무거나 입력하세요.");
System.out.println("입력한 값은 " + input + "입니다.");
for(int i=10; i>=1; i--) {
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package kr.or.ddit.basic;
import javax.swing.JOptionPane;
public class T06ThreadTest {
public static void main(String[] args) {
Thread th1 = new DataInput();
Thread th2 = new CountDown();
th1.start();
th2.start();
}
}
/**
* 사용자 입력을 받기 위한 스레드
*/
class DataInput extends Thread {
@Override
public void run() {
String input = JOptionPane.showInputDialog("아무거나 입력하세요.");
System.out.println("입력한 값은 " + input + "입니다.");
}
}
/**
* 카운트다운을 처리하기 위한 스레드
*/
class CountDown extends Thread {
@Override
public void run() {
for(int i=10; i>=1; i--) {
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package kr.or.ddit.basic;
import javax.swing.JOptionPane;
public class T06ThreadTest {
public static boolean inputCheck = false;
public static void main(String[] args) {
Thread th1 = new DataInput();
Thread th2 = new CountDown();
th1.start();
th2.start();
}
}
/**
* 사용자 입력을 받기 위한 스레드
*/
class DataInput extends Thread {
@Override
public void run() {
String input = JOptionPane.showInputDialog("아무거나 입력하세요.");
T06ThreadTest.inputCheck = true;
System.out.println("입력한 값은 " + input + "입니다.");
}
}
/**
* 카운트다운을 처리하기 위한 스레드
*/
class CountDown extends Thread {
@Override
public void run() {
for(int i=10; i>=1; i--) {
// 입력이 완료되었는지 여부를 확인하고 입력이 완료된 경우에는
// run()메서드를 종료한다.
if(T06ThreadTest.inputCheck) {
return; // run()메서드가 종료되면 해당 스레드도 종료된다.
}
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 10초가 경과되었는데도 입력이 없으면 프로그램을 종료한다.
System.out.println("10초가 지났습니다. 프로그램을 종료합니다.");
System.exit(0); // 프로그램 종료시키는 명령어
}
}
- setPriority() 이용 => 우선 순위 변경
package kr.or.ddit.basic;
public class T08ThreadPriorityTest {
public static void main(String[] args) {
System.out.println("최대 우선순위 : " + Thread.MAX_PRIORITY);
System.out.println("최소 우선순위 : " + Thread.MIN_PRIORITY);
System.out.println("보통 우선순위 : " + Thread.NORM_PRIORITY);
Thread[] ths = new Thread[] {
new ThreadTest1(),
new ThreadTest1(),
new ThreadTest1(),
new ThreadTest1(),
new ThreadTest1(),
new ThreadTest2()
};
// 우선 순위는 start()메서드를 호출하기 전에 설정해야 한다.
for(int i=0; i<ths.length; i++) {
if(i == 5) {
ths[i].setPriority(10);
} else {
ths[i].setPriority(1);
}
}
// 우선 순위 정보 출력하기
for (Thread th : ths) {
System.out.println(th.getName() + "의 우선 순위 : " + th.getPriority());
}
// 스레드 구동하기
for (Thread th : ths) {
th.start();
}
}
}
// 대문자를 출력하는 스레드
class ThreadTest1 extends Thread {
@Override
public void run() {
for(char ch='A'; ch<='Z'; ch++) {
System.out.println(ch);
// 아무것도 하지 않는 반복문(시간 때우기용)
for(long i=1; i<=1000000000L; i++) {}
}
}
}
// 소문자를 출력하는 스레드
class ThreadTest2 extends Thread {
@Override
public void run() {
for(char ch='a'; ch<='z'; ch++) {
System.out.println(ch);
// 아무것도 하지 않는 반복문(시간 때우기용)
for(long i=1; i<=1000000000L; i++) {}
}
}
}
- save() & setDaemon 이용 => 자동 저장 및 종료
Daemon Thread
: 다른 일반 스레드의 작업을 돕는 보조적인 스레드. 일반 스레드가 모두 종료되면 자동으로 종료된다.
start 메서드 호출 전에 설정해야 한다.
// start() 작동 전에 선언 필요!
스레드객체.setDaemon(true);
스레드객체.start();
package kr.or.ddit.basic;
public class T09ThreadDaemonTest {
public static void main(String[] args) {
AutoSaveThread autoSave = new AutoSaveThread();
// 데몬스레드 설정은 start()메서드 호출 전에 해주어야 한다.
autoSave.setDaemon(true);
autoSave.start();
for(int i=1; i<=20; i++) {
System.out.println("작업 " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("메인 스레드 종료...");
}
}
/**
* 자동 저장기능을 제공하는 스레드
*/
class AutoSaveThread extends Thread {
public void save() {
System.out.println("작업 내용을 저장합니다.");
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
save(); // 저장기능 호출
}
}
}
'자바' 카테고리의 다른 글
[Java 고급] 11장 동기화, Lock 객체, Collection 클래스 동기화 처리, wait() & notify() (0) | 2024.01.29 |
---|---|
[Java 고급] 10장 Thread상태, Thread 예시2 (1) | 2024.01.29 |
[Java 고급] 8장 Annotation (0) | 2024.01.27 |
[Java 고급] 7.5장 Test (0) | 2024.01.27 |
[Java 고급] 7장 enum (1) | 2024.01.27 |