프로세스, 스레드
- 프로세스
: 운영체제에서 실행 중인 하나의 애플리케이션
=> 애플리케이션 실행 시 운영체제로부터 메모리 할당받아 애플리케이션의 코드 실행
- 멀티 프로세스
: 운영체제 내 여러 프로세스 / 애플리케이션 단위의 멀티 태스킹
=> 운영체제에서 할당받은 각각의 메모리로 실행하기에 프로세스들은 서로 독립적
ex. 워드, 엑셀 동시 사용 중, 워드 오류 생겨도 엑셀 사용 가능
멀티프로세스⊃프로세스1⊃멀티 스레드
⊃프로세스2⊃싱글 스레드
- 멀티 스레드
: 한 프로세스 내 여러 스레드 (여러 코드 실행 흐름) / 애플리케이션 내부에서의 멀티 태스킹
=> 하나의 프로세스 내부에 생성됨 => 한 스레드에서 예외 발생시 전체 프로세스 종료 (다른 스레드 영향)
ex. 메신저 내에서 파일전송 스레드 예외 발생 시 채팅 스레드도 함께 종료되며 메신저 프로세스 자체가 종료
※
주기억장치 - RAM (메모리)
보조기억장치 - 하드디스크
중앙처리장치 - CPU
메인 스레드
: main() 메소드 실행 시 시작되는 스레드 (=메인 메소드)
모든 자바 애플리케이션에서 메인 스레드 반드시 존재함
작업 스레드 (객체) 생성 -> 병렬로 코드 실행 가능 (동시에 처리하는 것으로 보이지만 실제로는 번갈아가며 처리함)
실행 중인 스레드가 하나라도 있으면 프로세스 종료 X
=> 메인 스레드 종료, 작업 스레드 실행 중 -> 프로세스 종료 X
작업 스레드 생성, 실행
- 스레드 생성 전
import java.awt.Toolkit;
public class ThreadEx1 {
public static void main(String[] args) {
Toolkit toolkit = Toolkit.getDefaultToolkit(); //객체 생성, static
for (int i=1;i<=5;i++) {
toolkit.beep(); //비프음 발생
try {
Thread.sleep(500); //0.5초 일시정지(ms) //try, catch문 필수
} catch (InterruptedException e) {
}
}
for (int i=1;i<=5;i++) {
System.out.println("띵!!");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
}
}
=> 2개 for문 순차 실행
- java.lang.Thread 클래스로부터 직접 생성 - 1. 익명 객체 구현
import java.awt.Toolkit;
public class ThreadEx1 {
public static void main(String[] args) {
//작업 스레드
Thread thread = new Thread(new Runnable() {
//작업스레드 생성시 Runnable 인터페이스 구현 객체 만들어 대입
@Override
public void run() { //run() 재정의해 작업스레드 실행코드 작성
Toolkit toolkit = Toolkit.getDefaultToolkit();
for (int i=1;i<=5;i++) {
toolkit.beep();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
}
});
thread.start(); //스레드 시작 or 실행 대기 상태(다른 스레드 실행 시)
//메인 스레드
for (int i=1;i<=5;i++) {
System.out.println("띵!!");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
}
}
//
띵!!
띵!!
띵!!
띵!!
띵!!
=> 소리, 숫자 동시 실행
- java.lang.Thread 클래스로부터 직접 생성 - 2. 인터페이스 구현클래스 사용
import java.awt.Toolkit;
public class BeepTask implements Runnable { //Runnable 인터페이스 구현클래스
@Override
public void run() {
Toolkit toolkit = Toolkit.getDefaultToolkit();
for (int i=1;i<=5;i++) {
try {
toolkit.beep();
Thread.sleep(500);
} catch (InterruptedException e) {
}
}//for
}
}
import java.awt.Toolkit;
public class ThreadEx2 {
public static void main(String[] args) {
//작업 스레드
Runnable bt = new BeepTask();
//다형성 => Beeptask 생성, Runnable로 선언 => Runnable 내 메소드만 실행됨
Thread thread = new Thread(bt);
thread.start();
//메인 스레드
for (int i=1;i<=5;i++) {
System.out.println("띵!!");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
}
}
- Thread 하위클래스로부터 생성 - 3. Thread 상속
public class BeepThread extends Thread { //Thread 클래스 상속
@Override
public void run() {
Toolkit toolkit = Toolkit.getDefaultToolkit();
for (int i=1;i<=5;i++) {
try {
toolkit.beep();
Thread.sleep(500);
} catch (InterruptedException e) {
}
}//for
}
}
public class ThreadEx3 {
public static void main(String[] args) {
//작업 스레드
Thread thread = new BeepThread();
thread.start();
//메인 스레드
for (int i=1;i<=5;i++) {
System.out.println("띵!!");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
}
}
스레드 이름
메인스레드 이름 => main
작업스레드 생성 시 "Thread-n" 자동 설정
=> Thread 클래스 setName() 메소드로 변경 가능 (cf. getName() 으로 이름 전달받음)
setName(), getName()
=> Thread의 인스턴스 메소드 (스레드 객체 참조 필요)
=> 객체 참조 없이는 Thread 정적 메소드 currentThread() 사용해 현재 실행되는 스레드 참조 얻기 가능
public class ThreadA extends Thread{
//생성자
public ThreadA() {
setName("ThreadA"); //인스턴스 메소드
}
@Override
public void run() {
for (int i=1;i<=5;i++) {
System.out.println(getName()+"가 한 작업");
}
}
}
public class ThreadB extends Thread {
//기본 생성자
@Override
public void run() {
for (int i=1;i<=5;i++) {
System.out.println(getName()+"가 한 작업"); //Thread-1
}
}
}
public class ThreadEx4 {
public static void main(String[] args) {
Thread mainThread = Thread.currentThread();
//코드 실행하는 현재 스레드의 참조객체 얻음 //현재의 스레드가 무엇인지 확인
System.out.println("프로그램 시작 스레드 이름 : "+ mainThread);
ThreadA ta = new ThreadA(); //스레드 생성
System.out.println("작업 스레드 이름 : "+ta.getName());
ta.start();
ThreadB tb = new ThreadB();
System.out.println("작업 스레드 이름 : "+tb.getName());
tb.start();
}
}
//
프로그램 시작 스레드 이름 : Thread[main,5,main]
작업 스레드 이름 : ThreadA
ThreadA가 한 작업
ThreadA가 한 작업
ThreadA가 한 작업
ThreadA가 한 작업
ThreadA가 한 작업
작업 스레드 이름 : Thread-1
Thread-1가 한 작업
Thread-1가 한 작업
Thread-1가 한 작업
Thread-1가 한 작업
Thread-1가 한 작업
스레드 우선순위
스레드 개수가 코어의 수보다 많을 때, 스레드를 어떤 순서로 실행할지 결정 => 스레드 스케줄링
-> 번갈아가며 스레드들의 run() 메소드 조금씩 실행
- setPriority()
: 우선순위 부여하는 메소드
우선순위 1~10까지 부여되며, 숫자 클수록 우선순위 높음
thread.setPriority(우선순위);
ex.
thread.setPriority(Thread.MAX_PRIORITY); //10
thread.setPriority(Thread.NORM_PRIORITY); //5
thread.setPriority(Thread.MIN_PRIORITY); //1
public class CalThread extends Thread{
//생성자
public CalThread(String name) {
setName(name); //스레드 이름 변경
}
@Override
public void run() { //스레드 실행 내용
for (long i=0;i<1100000000L;i++) {
}
System.out.println(getName());
}
}
public class PriorityEx1 {
public static void main(String[] args) {
for (int i=1;i<=10;i++) {
Thread thread = new CalThread("계산 작업 "+i);
if (i==7) {
thread.setPriority(Thread.MAX_PRIORITY);
}else {
thread.setPriority(Thread.MIN_PRIORITY);
}
thread.start();
}//for
}
}
//
계산 작업 7
계산 작업 8
계산 작업 3
계산 작업 4
계산 작업 2
계산 작업 10
계산 작업 6
계산 작업 9
계산 작업 5
계산 작업 1
=> 7이 1순위로 끝남
동기화 메소드 (synchronized)
: 2개 이상의 스레드가 클래스 객체 공유할 때, 사용 중인 객체를 다른 스레드가 변경할 수 없도록 잠금
임계 영역 : 단 하나의 스레드만 실행할 수 있는 코드 영역
cf. 동기화 블록 (동기화 메소드가 더 활용도 높음)
void method(){
..
synchronized(공유객체){ => 메소드 내 특정 블록만 동시 실행 불가
}
..
}
- ex. 계산기 클래스 공유
public class Calculator {
private int memory; //접근제한 -> getter, setter 필요
public int getMemory() {
return memory;
}
public void setMemory(int memory) {
this.memory = memory;
try {
Thread.sleep(2000); //2초
} catch (InterruptedException e) { //튕김 현상 예외 처리
}
System.out.println(Thread.currentThread().getName()+" : "+this.memory);
//현재 실행하고 있는 스레드의 이름
}
}
public class UserJob1 extends Thread {
//객체 필드
private Calculator calc; //user1 & user2 계산기 공유
public void setCalc(Calculator calc) {
this.setName("UserJob1");
this.calc = calc;
}
@Override
public void run() {
calc.setMemory(100);
}
}
public class UserJob2 extends Thread {
//객체 필드
private Calculator calc; //user1 & user2 계산기 공유
public void setCalc(Calculator calc) {
this.setName("UserJob2");
this.calc = calc;
}
@Override
public void run() {
calc.setMemory(50);
}
}
public class CalculatorEx1 {
public static void main(String[] args) {
Calculator calc = new Calculator(); //인스턴스 객체 생성
UserJob1 job1 = new UserJob1(); //스레드 생성
job1.setCalc(calc);
job1.start();
UserJob2 job2 = new UserJob2(); //스레드 생성
job2.setCalc(calc);
job2.start();
}
}
//
UserJob2 : 50
UserJob1 : 50 => 1번 스레드 실행 중에 2번 스레드 실행돼 오류
=> 해결
- Calculator 클래스 동기화 메소드
public class Calculator {
private int memory;
public int getMemory() {
return memory;
}
public synchronized void setMemory(int memory) { //공유 객체
//임계 영역 : 단 하나의 스레드만 실행
this.memory = memory;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName()+" : "+this.memory);
}
}
//
UserJob1 : 100
UserJob2 : 50
스레드 상태
Thread 클래스의 getState() 메소드 통해 스레드 상태 얻을 수 있음
NEW : 스레드 객체 생성
RUNNABLE : 실행 또는 실행대기
TIME_WAITING : sleep
BLOCKED : 동기화블록으로 잠금됨
TERMINATED : 실행 마침
public class TargetThread extends Thread{
@Override
public void run() {
//작업 1
for (long i=0;i<3100000000L;i++) {
}
//1초 일시정지
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
//작업 2
for (long i=0;i<3100000000L;i++) {
}
}
}
public class StatePrintThread extends Thread {
private Thread target;
public StatePrintThread(Thread target) {
this.target=target;
}
@Override
public void run() {
while(true) {
Thread.State state = target.getState(); //상태 가져오기
System.out.println("타겟 스레드 상태 "+state);
if (state==Thread.State.NEW) { //스레드 생성
target.start(); //스레드 실행
}
if(state==Thread.State.TERMINATED) { //스레드 마침 => while 탈출
break;
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
}
}
public class ThreadStateEx1 {
public static void main(String[] args) {
StatePrintThread stateThread = new StatePrintThread(new TargetThread());
stateThread.start(); //스레드 실행
}
}
//
타겟 스레드 상태 NEW
타겟 스레드 상태 RUNNABLE
타겟 스레드 상태 RUNNABLE
타겟 스레드 상태 TIMED_WAITING
타겟 스레드 상태 TIMED_WAITING
타겟 스레드 상태 RUNNABLE
타겟 스레드 상태 RUNNABLE
타겟 스레드 상태 TERMINATED
스레드 상태 제어
- sleep() - 일시 정지
: 실행 중인 스레드 일정 시간 멈춤
- yield() - 실행 양보
public class ThreadJob1_yield extends Thread{
public boolean stop=false; //종료 여부
public boolean work=true; //작업 진행 여부
@Override
public void run() {
while(!stop) {
if (work) {
System.out.println("ThreadJob1 일 작업중");
}else {
Thread.yield(); //다른 스레드에게 실행 양보
}
}
System.out.println("ThreadJob1 종료");
}
}
public class ThreadJob2_yield extends Thread{
public boolean stop=false; //종료 여부
public boolean work=true; //작업 진행 여부
@Override
public void run() {
while(!stop) {
if (work) {
System.out.println("ThreadJob2 일 작업중");
}else {
Thread.yield(); //다른 스레드에게 실행 양보
}
}
System.out.println("ThreadJob2 종료");
}
}
public class YieldEx1 {
public static void main(String[] args) {
ThreadJob1_yield tJob1 = new ThreadJob1_yield();
ThreadJob2_yield tJob2 = new ThreadJob2_yield();
//2개 스레드 동시 실행
tJob1.start();
tJob2.start();
//메인 스레드 2초 동안 일시정지 / 작업스레드 1 & 2 실행
try {
Thread.sleep(2000);
} catch (InterruptedException e) {}
tJob1.work=false; //tJob2 스레드만 실행
//메인 스레드 2초 동안 일시정지 / 2 실행
try {
Thread.sleep(2000);
} catch (InterruptedException e) {}
tJob1.work=true; //tJob1 스레드 재실행
//메인 스레드 2초 동안 일시정지 / 1 & 2 실행
try {
Thread.sleep(2000);
} catch (InterruptedException e) {}
tJob1.stop = true; //스레드 종료
tJob2.stop = true; //
}
}
//
1, 2 동시 (2초)
2만 (2초)
1, 2 동시 (2초)
ThreadJob2 종료
ThreadJob1 종료
- join() - 다른 스레드의 종료 기다림
다른 스레드 종료된 후 코드 실행
public class SumThread extends Thread {
private int sum; //getter, setter 필요
public int getSum() {
return sum;
}
@Override
public void run() {
for (int i=1;i<=100;i++) {
sum+=i;
}
}
}
public class JoinEx1 {
public static void main(String[] args) {
//작업스레드
SumThread sumt = new SumThread(); //스레드 생성
sumt.start();
//메인스레드
System.out.print("1~100 합 : "+sumt.getSum());
}
}
//
1~100 합 : 0 //start하자마자 run 메소드, 출력 메소드 거의 동시 실행 => sum에 값 안 들어왔을 때 출력
=> 해결
public class JoinEx1 {
public static void main(String[] args) {
SumThread sumt = new SumThread(); //작업스레드 생성
sumt.start();
try {
sumt.join(); //메인스레드 일시 정지 (sumt 스레드 실행 끝날 때까지 기다림)
} catch (InterruptedException e) {
}
System.out.print("1~100 합 : "+sumt.getSum());
}
}
//
1~100 합 : 5050
- wait(), notify(), notifyAll() - 스레드 간 협업
공유 객체 => 두 스레드가 작업할 내용을 각각 동기화 메소드로 구분 -> 교대로 번갈아가며 실행
스레드A 작업 완료 => notify() => 스레드B 실행대기 상태로 (일시정지에서) => wait() => 스레드A 일시정지
wait() 대신 wait(long timeout) / wait(long timeout, int nanos)
=> notify() 호출 없이 지정시간 지나면 일시정지 스레드가 실행대기 상태로 자동 변경
notifyAll()
=> wait()에 의해 일시정지된 모든 스레드들 실행대기 상태로 변경
cf. notify : 한 개 스레드만 실행대기 상태로 변경
public class ThreadWorkA extends Thread {
private WorkObject object;
public ThreadWorkA(WorkObject object) {
this.object=object;
}
@Override
public void run() {
for (int i=1;i<=10;i++) {
object.methodA();
}
}
}
public class ThreadWorkB extends Thread {
private WorkObject object;
public ThreadWorkB(WorkObject object) {
this.object=object;
}
@Override
public void run() {
for (int i=1;i<=10;i++) {
object.methodB();
}
}
}
//ThreadWorkA, ThreadWorkB의 공유 객체
public class WorkObject {
public void methodA() {
System.out.println("ThreadWorkA의 작업 실행");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
}
}
public void methodB() {
System.out.println("ThreadWorkB의 작업 실행");
try {
Thread.sleep(300); //sleep 없으면 너무 빨라서 A B 순차 출력됨
} catch (InterruptedException e) {
}
}
}
public class WorkObjectEx1 {
public static void main(String[] args) {
WorkObject shared = new WorkObject(); //공유 객체 생성
ThreadWorkA workA = new ThreadWorkA(shared);
ThreadWorkB workB = new ThreadWorkB(shared);
workA.start();
workB.start();
}
}
//
ThreadWorkA의 작업 실행
ThreadWorkA의 작업 실행
ThreadWorkA의 작업 실행
~
ThreadWorkA의 작업 실행
ThreadWorkA의 작업 실행
ThreadWorkA의 작업 실행
ThreadWorkB의 작업 실행
ThreadWorkB의 작업 실행
ThreadWorkB의 작업 실행
~
ThreadWorkB의 작업 실행
ThreadWorkB의 작업 실행
ThreadWorkB의 작업 실행
=> sleep 있으면 섞여서 출력 / 없으면 주로 순차 출력
=> 협업
//ThreadWorkA, ThreadWorkB의 공유 객체
public class WorkObject {
public synchronized void methodA() { //동기화 메소드 (A부터 실행됨)
System.out.println("ThreadWorkA의 작업 실행");
notify();
//일시정지->실행대기 => 다른 동기화 메소드에 적용 (각 메소드마다 한번씩 수행하도록)
try {
wait(); //ThreadWorkA 일시정지
} catch (InterruptedException e) {
}
}
public synchronized void methodB() {
System.out.println("ThreadWorkB의 작업 실행");
notify();
try {
wait(); //ThreadWorkB 일시정지
} catch (InterruptedException e) {
}
}
}
//
ThreadWorkA의 작업 실행
ThreadWorkB의 작업 실행
ThreadWorkA의 작업 실행
~
ThreadWorkB의 작업 실행
ThreadWorkA의 작업 실행
ThreadWorkB의 작업 실행
- stop 플래그, interrupt() - 스레드의 안전한 종료
run() 메소드 모두 실행시 스레드는 자동 종료 / 실행 중인 스레드를 즉시 종료할 때에는 stop 플래그 or interrupt()
stop 플래그 : boolean 이용해 run() 메소드 종료 유도
interrupt() : 스레드가 sleep() 메소드로 일시정지 상태에 있을 때 => InterruptedException 예외 발생시킴
1. Stop 플래그
public class StopThread extends Thread {
private boolean stop; //stop 플래그 필드
public void setStop(boolean stop) { //
this.stop = stop;
}
@Override
public void run() {
while(!stop) { //setStop(true) => while문 탈출
System.out.println("현재 작업중..");
}
System.out.println("자원 정리");
System.out.println("실행 종료");
}
}
public class StopThreadEx1 {
public static void main(String[] args) {
StopThread stopthread = new StopThread();
stopthread.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {}
stopthread.setStop(true); //
}
}
//
현재 작업중..
현재 작업중..
현재 작업중..
현재 작업중..
~
현재 작업중..
현재 작업중..
자원 정리
실행 종료
2. interrupt 메소드
public class InterruptThread extends Thread {
@Override
public void run() {
try {
while(true) {
System.out.println("현재 작업 중");
Thread.sleep(1); //sleep()으로 일시정지
}
} catch(InterruptedException e) {} //일시정지 상태에서 예외 발생 => while 탈출
System.out.println("자원 종료");
System.out.println("실행 종료");
}
}
public class InterruptThreadEx1 {
public static void main(String[] args) {
InterruptThread thread1 = new InterruptThread();
thread1.start();
try {
Thread.sleep(1000); //메인스레드 1초 일시정지 (작업스레드 1초 실행)
} catch (InterruptedException e) {}
thread1.interrupt(); //일시정지 시 예외 발생시키기 (1초 후)
}
}
//
현재 작업 중
현재 작업 중
현재 작업 중
현재 작업 중
~
현재 작업 중
현재 작업 중
현재 작업 중
자원 종료
실행 종료
데몬 스레드 : 주 스레드의 작업을 돕는 보조역할
주 스레드 종료 시 데몬 스레드 강제 자동 종료됨
public class AutoSaveThread extends Thread {
public void save() {
System.out.println("작업 내용을 저장");
}
@Override
public void run() {
while(true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
save(); //1초마다 실행
}
}
}
public class AutoSaveThreadEx1 {
public static void main(String[] args) {
AutoSaveThread autoSave = new AutoSaveThread();
autoSave.start();
try {
Thread.sleep(3000); //3초간 작업스레드 실행
} catch (InterruptedException e) {}
System.out.println("메인 스레드 종료 !");
}
}
//
작업 내용을 저장
작업 내용을 저장
메인 스레드 종료 !
작업 내용을 저장
작업 내용을 저장
~ 반복
=> 해결
- 데몬 스레드 사용
public class AutoSaveThreadEx1 {
public static void main(String[] args) {
AutoSaveThread autoSave = new AutoSaveThread();
autoSave.setDaemon(true); //start() 전 //데몬스레드로 세팅
autoSave.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {}
System.out.println("메인 스레드 종료 !");
}
}
//
작업 내용을 저장
작업 내용을 저장
메인 스레드 종료 !
'Programming > 자바' 카테고리의 다른 글
자바 재정리 - 람다식 (0) | 2022.09.08 |
---|---|
자바 재정리 - 제네릭 (0) | 2022.09.08 |
자바 재정리 - API (0) | 2022.09.05 |
자바 재정리 - 예외 처리 (0) | 2022.08.26 |
자바 재정리 - 중첩 클래스, 중첩 인터페이스 (0) | 2022.08.26 |