프로세스와 스레드
- 프로세스 : 실행중인 프로그램, 프로그램 하나가 실행되는 단위
- 스레드 : 하나의 프로세스 안에서 실질적인 작업 처리하는 하나의 단위
- 프로그램을 수행하는데 필요한 데이터, 메모리등의 자원과 스레드로 구성
- 프로세스의 자원을 이용해서 실제로 작업을 수행하는 것이 스레드
- 둘 이상의 스레드를 가진 프로세스를 멀티스레드 프로세스라고 함
- CPU가 아주 짧은 시간 동안 여러 작업 번갈 아 수행으로 동시 작업 처럼 보임
- 서버프로그래밍(서블릿)의 겨우 여러개의 스레드를 생성 사용자의 요청 일대일 대응
- 싱글스레드로 작성 시 프로세스 생성 비용이 많이 듬(시간, 메모리 공간등)
- 단점으로 여러 스레드가 같은 프로세스의 자원을 공유 함으로 동기화, 교착상태 등 고려 해야함
- 서블릿의 겨우 WAS(Web Application Server) 관리 해줌
- 멀티태스킹 : 여러개의 프로세스 동시 실행
스레드의 생성 및 실행
Thread 클래스 상속받아서 스레드 생성
- 해당 클래스 자체가 스레드가되기 때문에 스레드 생성자를 호출 하여 바로 생성 가능
- start() 호출 -> 오버라이딩한 run() 자동 호출
- main() 스레드 와 run() 스레드 2개가 생김
class ThreadExtend extends Thread {
public void run() {
System.out.println("Thread 상속받는 방법");
for (int i=0; i<50; i++) {
System.out.println("ThreadExtend:"+i);
}
}
}
Runnable 인터페이스 구현으로 스레드 정의
- 다중 상속이 안되므로 이경우가 더 많이 쓰임
- Runnable 인터페이스에는 run() 만 정의 되어 있음
- 스레드 객체가 아니므로 스레드 객체에 생성자로 전달 해서 사용
class RunnableImple implements Runnable {
public void run() {
System.out.println("Runnable 구현하는 방법");
for (int i=0; i<50; i++) {
System.out.println("RunnableImple:"+i);
}
}
}
스레스 스타트 start()
- run() 메서드로 정의 하고 start()로 실행
package chapter17;
public class ThreadEx {
public static void main(String[] args) {
// Thread 상속받는 방법
ThreadExtend t1 = new ThreadExtend();
// Runnable 구현하는 방법
Runnable r = new RunnableImple();
// Thread 생성자의 매개변수로 전달
Thread t2 = new Thread(r);
t1.start();
t2.start();
}
Runnable 인터페이스 익명 클래스 구현으로 스레드 정의
package chapter17;
public class ThreadEx2 {
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("t1 스레드 시작");
for (int i=0; i<50; i++) {
System.out.println("t1 : "+i);
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("t2 스레드 시작");
for (int i=0; i<50; i++) {
System.out.println("t2 : "+i);
}
}
});
t1.start();
t2.start();
}
}
람다표현식으로 스레드 정의
package chapter17;
public class ThreadEx3 {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
System.out.println("t1 스레드 시작");
for (int i=0; i<50; i++) {
System.out.println("t1 : "+i);
}
});
Thread t2 = new Thread(() -> {
System.out.println("t2 스레드 시작");
for (int i=0; i<50; i++) {
System.out.println("t2 : "+i);
}
});
t1.start();
t2.start();
}
}
스레드 우선순위
- 스레드가 여러개 실행할 때는 정확한 순서 제어가 힘듬
- 스레드 스케줄러에 의해 cpu 사용이 결정됨
- 정확하게 제어할 수는 없으나 우선순위가 높은 스레드가 CPU를 더 많이 점유
- 스레드가 수행하는 작업의 중요도에 따라 스레드의 우선순위 지정하여 특정 스레드가 더 많은 작업시간을 갖도록 함
- setPriority(우선순위값) 메서드 사용
- 우선순위값 1~7(윈도우는 10), 기본값 5
- static int Min_PRIORITY : 최하 우선순위
- static int MAX_PRIORITY : 최대 우선순위
- static int Norm_PRIORITY : 중간 우선순위(디폴트)
package chapter17;
public class ThreadEx4 {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
System.out.println("t1 스레드 시작");
for (int i=0; i<50; i++) {
System.out.println("t1 : "+i);
}
});
Thread t2 = new Thread(() -> {
System.out.println("t2 스레드 시작");
for (int i=0; i<50; i++) {
System.out.println("t2 : "+i);
}
});
// 우선순위 지정
t1.setPriority(MAX_PRIORITY ); // t1이 먼저 종료됨
t2.setPriority(3);
t1.start();
t2.start();
}
}
쓰레드 상태 변화 및 제어
- run, blocked, dead 세 가지 상태 중 하나의 상태로 머무름
- crate(쓰레드 생성) -> start() -> runnable(실행 가능상태) -> run() -> running(실행된 상태)
- running -> sleep(), wait(), yield() -> blocked(실행 중지,대기 상태) -> notify() -> runnable(실행 가능상태)
- running -> dead (작업 완료, 메모리 삭제)
sleep(n)
- 실행중인 스레드를 원하는 시간만큼 멈추기, 1/1000 * n 초
package chapter17;
public class ThreadEx7 {
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i=0; i<10; i++) {
System.out.println("t1:"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
}
System.out.println("스레드 실행 종료");
}
});
t1.start();
}
}
join() 메서드
- 다른 스레드의 실행이 완료될때까지 기다리는 메서드
class Sum extends Thread {
int sum = 0;
@Override
public void run() {
for (int i=1; i<=100; i++) {
sum += i;
}
}
}
2개의 스레드 처리
package chapter17;
public class ThreadEx8 {
public static void main(String[] args) {
Sum t1 = new Sum();
Sum t2 = new Sum();
t1.start();
t2.start();
try {
t1.join(); // t1 스레드가 종료될때까지 대기
t2.join(); // t2 스레드가 종료될때까지 대기
} catch (InterruptedException e) {
}
System.out.println("두 스레드의 sum 합계 = "+(t1.sum+t2.sum));
}
}
yield() 메서드
- 다른 스레드에게 실행을 양보하는 메서드
package chapter17;
public class ThreadEx9 {
public static void main(String[] args) {
YieldThread t1 = new YieldThread();
YieldThread t2 = new YieldThread();
t1.start();
t2.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
t1.isContinue = false; // t1 양보
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
t1.isContinue = true; // t1 다시 실행, 주석달면 t2만 실행되고 끝남
try {
Thread.sleep(2000);
} catch (InterruptedException e) {}
// 스레드 종료
t1.isBreak = true;
t2.isBreak = true;
}
}
class YieldThread extends Thread {
boolean isBreak = false;
boolean isContinue = true;
@Override
public void run() {
while(!isBreak) {
if (isContinue) {
System.out.println(getName()+" 실행 중");
}else {
Thread.yield();
}
}
System.out.println(getName()+" 종료");
}
}
스레드 동기화
- 하나의 객체를 여러 스레드가 동시에 사용시 의도치 않은 값의 변경등이 발생
- 하나의 스레드 작업이 끝날 때까지 해당 객체가 변경되지 못하도록 막는것
- 여러 개의 스레드가 한 개의 자원을 사용하고자 할 때 해당 스레드만 제외하고 나머지는 접근을 못하도록 막는 것
- ex) 예매 시스템 등
- syncronized 키워드 사용
// 플레이어1 스레드
class Player1 extends Thread {
private SmartPhoneGame game;
public void setSmartPhoneGame(SmartPhoneGame game) {
this.setName("Player1"); // 스레드 이름 지정
this.game = game;
}
@Override
public void run() {
game.increaseLevel();
}
}
// 플레이어2 스레드
class Player2 extends Thread {
private SmartPhoneGame game;
public void setSmartPhoneGame(SmartPhoneGame game) {
this.setName("Player2"); // 스레드 이름 지정
this.game = game;
}
@Override
public void run() {
game.increaseLevel();
}
}
동기화가 되지 않은 경우
// 스마트폰게임 클래스
class SmartPhoneGame {
private int level; // 레벨
public int getLevel() {
return this.level;
}
public void increaseLevel() {
while (true) {
this.level++; // 레벨 1씩 증가
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
// 현재 스레드의 이름과 레벌 출력
System.out.println(Thread.currentThread().getName()+" Level : " + this.level);
// 레벨이 10의 배수가 되면 종료
if (this.level % 10 == 0) break;
}
}
}
테스트
package chapter17;
public class ThreadEx10 {
public static void main(String[] args) {
// 게임 객체 생성
SmartPhoneGame game = new SmartPhoneGame();
// 플레이어1 객체 생성 후 스레드 실행
Player1 p1 = new Player1();
p1.setSmartPhoneGame(game);
p1.start();
// 플레이어2 객체 생성 후 스레드 실행
Player2 p2 = new Player2();
p2.setSmartPhoneGame(game);
p2.start();
}
}
동기화한 경우
- 메서드에 synchronized 사용
// 스마트폰게임 클래스
class SmartPhoneGame {
private int level; // 레벨
public int getLevel() {
return this.level;
}
public synchronized void increaseLevel() {
while (true) {
this.level++; // 레벨 1씩 증가
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
// 현재 스레드의 이름과 레벌 출력
System.out.println(Thread.currentThread().getName()+" Level : " + this.level);
// 레벨이 10의 배수가 되면 종료
if (this.level % 10 == 0) break;
}
}
스레드 간에 교대로 작업 (협업)
- running -> wait() -> blocked(실행 중지,대기 상태) -> notify() -> runnable(실행 가능상태)
- wait() : 실행 -> 대기 상태
- notify(), notifyAll() : 대기 -> 실행 가능
// 스마트폰게임 클래스
class SmartPhoneGame {
private int level; // 레벨
public int getLevel() {
return this.level;
}
public synchronized void increaseLevel() {
while (true) {
this.level++; // 레벨 1씩 증가
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
// 현재 스레드의 이름과 레벌 출력
System.out.println(Thread.currentThread().getName()+" Level : " + this.level);
if (this.level == 5) {
try {
wait(); // 실행 -> 대기 상태
notifyAll(); // 대기 -> 실행 가능
} catch (InterruptedException e) {}
break;
}
// 레벨이 10의 배수가 되면 종료
if (this.level % 10 == 0) break;
}
}
}
스레드 예제
- 입금후 출금처리
// 엄마 스레드
class Mother extends Thread {
Account account;
Mother(Account account) {
super("엄마");
this.account = account;
}
@Override
public void run() {
while(true) {
try {
account.deposit();
sleep((int)(Math.random()*2000));
} catch (InterruptedException e) {
break;
}
}
}
}
//아들 스레드
class Son extends Thread {
Account account;
Son(Account account) {
super("아들");
this.account = account;
}
@Override
public void run() {
while(true) {
try {
account.withdraw();
sleep((int)(Math.random()*300));
} catch (InterruptedException e) {
break;
}
}
}
}
// 통장 클래스
class Account {
int money;
synchronized void withdraw() {
while(money == 0) {
try {
wait();
}catch(InterruptedException e) {
break;
}
}
notifyAll();
if(money > 0) {
System.out.println(Thread.currentThread().getName() + money + "원 출금");
money = 0;
}
}
synchronized void deposit() {
while(money > 0) {
try {
wait();
} catch(InterruptedException e) {
break;
}
}
// 랜덤 입금 1~5만원
money = (int)((Math.random()*5)+1)*10000;
notifyAll();
System.out.println();
System.out.println(Thread.currentThread().getName() + money + "원 입금");
}
}
package chapter17;
public class ThreadEx14 {
public static void main(String[] args) {
// 통장 객체 생성
Account acc = new Account();
// 엄마스레드 객체 생성
Mother mother = new Mother(acc);
// 아들스레드 객체 생성
Son son = new Son(acc);
mother.start();
son.start();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {}
// 스레드 중지
son.interrupt();
mother.interrupt();
}
}
'Java' 카테고리의 다른 글
네트워크 network (0) | 2022.03.07 |
---|---|
입출력 Input / Output (0) | 2022.03.07 |
스트림 stream (0) | 2022.03.07 |
람다 lambda (0) | 2022.03.07 |
제네릭 generic (0) | 2022.03.07 |