본문 바로가기
Back/JAVA

프로세스(Process)와 스레드(Thread)

by 시월해 2021. 3. 24.

* 프로세스(process)?

 


  - 현재 CPU에 의해서 처리되는 프로그램.
  - 운영체제로부터 메모리를 할당을 받음.
  - 자바에서는 하나의 클래스를 의미함.
  - 무한반복을 가진 프로세스를 실행할 경우
    CPU가 해당 프로세스를 놓지 않기 때문에
    다음 프로세스를 실행할 수가 없게 됨.
  - 이러한 문제점을 개성하기 위해서
    Thread 개념이 도입이 되었음.

 

 

* 스레드(Thread)?


  - Process를 작은 단위로 쪼개어 놓은 작업 단위.
    ==> 실제 프로그램이 수행되는 작업의 최소 단위.
  - 스레드는 병행 처리를 지원함(멀티태스킹).
  - 응용분야 : 게임, 채팅 등등

 


 * [스레드 클래스 작성 방법 - 2가지]


  1. Thread라는 클래스 상속(extends)하는 방법.
     1) Thread라는 클래스를 상속을 받음.
     2) run() 메서드를 재정의. - 스레드 구현.
     3-1) 스레드 클래스 객체 생성.
     3-2) 참조변수.start() 메서드 호출 ==> run() 메서드 실행.

 

 2. Runnable 인터페이스를 구현(implements) 하는 방법.
    1) Runnable 인터페이스를 상속(implements)
    2) run() 추상메서드 재정의 ==> 강제성
    3) 스레드 클래스(자식클래스) 객체 생성.
    4) Thread 라는 클래스 객체 생성.
        ==> Thread 라는 클래스의 생성자의 인자에
            자식클래스의 참조 변수를 기재.
     5) Thread 라는 클래스의 참조변수.start() 메서드 호출

 

 

 * 무명 클래스(anonymous class)를 이용한 스레드 생성의 장점.


  - 코드의 집중화로 가독성이 높아진다.
  - 별도의 상속 과정이 필요가 없어진다.

 

 

 * 멀티 스레드와 스레드 이름 지정.


  - 멀티 스레드 : 하나의 스레드 클래스를 대상으로
              2개 이상의 스레드 객체를 생성하는 기법.
  - 스레드 이름 지정 : 멀티스레드에서 생성한 스레드 객체에
                 이름을 부여하는 과정.
                 형식) super(name);
  - 멀티 스레드는 자신의 이름을 가지고 있음.
  - 스레드의 이름이 큰 역할을 하지는 않지만, 디버깅 시
    어떤 스레드가 현재 실행되어 작업하는지를 조사(확인)할
    목적으로 많이 사용됨. 

 

 * synchronized 키워드


 * - 스레드 동기화와 관련된 키워드.
 * - 멀티스레드에 의해서 공유된 메서드나 클래스를 대상으로
 *   임계영역(critical section)을 설정.
 * - 임계영역으로 설정된 메서드에서는 가장 먼저 도착한 스레드가
 *   완전히 종료될 때까지 나머지 스레드는 대기하는 명령어.
 * - 공유되는 자원은 대부분 static 키워드를 가진 객체들을 사용함.


기본예제1) 쓰레드를 사용하지 않는 무한 반복 코딩의 경우 프로그램이 제대로 작동하지 않음

package sist;

class Process1 {
	
	void go() {
		int i = 1;
		while(true) {
			System.out.println("i >>> " + i);
			i++;
		}
	}
}

class Process2 {
	
	void go() {
		int j = 1;
		while(true) {
			System.out.println("j >>> " + j);
			j++;
		}
	}
}

public class Ex01 {

	public static void main(String[] args) {
	
		Process1 p1 = new Process1();
		Process2 p2 = new Process2();
		
		p1.go(); p2.go();

	}

}

i와 j가 동시에 돌아가도록 코딩했지만 결과에 j값이 없다.


예제1) Thread라는 클래스를 이용한 스레드 생성

// Thread라는 클래스 상속(extends)하는 방법
class Thread1 extends Thread {  // 1-1)
	
	/*void go() {
		int i = 1;
		while(true) {
			System.out.println("i >>> " + i);
			i++;
		}
	}*/
	
	@Override
	public void run() {      // 1-2)
		
		int i = 1;
		while(true) {
			System.out.println("i >>> " + i);
			i++;
		}
	}
}


class Thread2 extends Thread {
	
	/*void go() {
		int j = 1;
		while(true) {
			System.out.println("j >>> " + j);
			j++;
		}
	}*/
	
	@Override
	public void run() {
		int j = 1;
		while(true) {
			System.out.println("j >>> " + j);
			j++;
		}
	}
}

public class Ex02 {	
	
	public static void main(String[] args) {
		
		// 3-1) 스레드 클래스 객체 생성.
		Thread1 t1 = new Thread1();
		Thread2 t2 = new Thread2();
		
		// 3-2) 참조변수.start() 메서드 호출 ==> run() 메서드 실행.
		t1.start(); t2.start();

	}

}

쓰레드를 사용해 코딩하면 무한반복이어도 i와 j값이 동시에 증가한다.


예제2) Runnable 인터페이스를 이용한 스레드 생성

// Runnable 인터페이스로 구현(implements)하는 방법.
package sist;

class Runnable1 implements Runnable {

	@Override
	public void run() {
		
		int i = 1;
		while(true) {
			System.out.println("i >>> " + i);
			i++;
		}
		
	}
	
}

class Runnable2 implements Runnable {

	@Override
	public void run() {
		
		int j = 1;
		while(true) {
			System.out.println("j >>> " + j);
			j++;
		}
		
	}
	
}

public class Ex03 {

	public static void main(String[] args) {
		
		// 2-3) 스레드 클래스(자식클래스) 객체 생성.
		Runnable1 r1 = new Runnable1();
		Runnable2 r2 = new Runnable2();
		
		// 2-4) Thread 라는 클래스 객체 생성.
		Thread t1 = new Thread(r1);
		Thread t2 = new Thread(r2);
		
		// 2-5) Thread 라는 클래스의 참조변수.start() 메서드 호출
		t1.start(); t2.start();

	}

}

 

예제1 소스를 Runnable 인터페이스로 구현한 결과


예제3) 무명 클래스를 이용한 스레드 생성

package sist;

public class Ex04 {

	public static void main(String[] args) {
		
		new Thread() {
			@Override
			public void run() {
				int i = 1;
				while(true) {
					System.out.println("i >>> " + i);
					i++;
				}
			}
		}.start();
		
		
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				int j = 1;
				while(true) {
					System.out.println("j >>> " + j);
					j++;
				}
				
			}
		}).start();

	}

}

예제1 소스를 무명클래스로 구현한 결과


에제4) 멀티스레드와 스레드 이름 지정

package sist;

/*
 * [ATM 클래스 설계서]
 * - 입금과 출금의 은행거래가 가능한 ATM 공용 클래스 설계.
 * - 계좌잔액 : private int balance;
 * - 생성자 : 계좌잔액(balance) 초기화
 * - deposit() : 외부 클래스에서 입금하는 메서드.
 *   예) balance = balance + money;  // 잔액에 입금액을 더함.
 *       출력문 : OO,OOO원을 입금하여 OO,OOO원이 남음.
 * - withdraw() : 외부 클래스에서 출금하는 메서드.
 *   예) balance = balance - money;  // 잔액에서 출금액을 차감.
 *       출력문 : OO,OOO원을 출금하여 OO,OOO원이 남음.
 *       (단, balance < money 인 경우 "잔액이 부족합니다." 메세지 출력)
 */

class ATM {  // 공용 클래스

	private int balance;    // 계좌 잔액
	
	public ATM() {  }
	
	public ATM(int balance) {
		this.balance = balance;
	}
	
	// 입금하는 메서드
	synchronized void deposit(int money) {
		balance = balance + money;
		System.out.println
			(Thread.currentThread().getName()+"님이 "+
					money+"원을 입금하여 "+balance+"원이 남았습니다.");
	}
	
	// 출금하는 메서드
	synchronized void withdraw(int money) {
		if(money > balance) {
			System.out.println("잔액이 부족합니다.");
			return;  // 현재 실행되고 있는 메서드를 종료하는 명령어.
		}
		
		balance = balance - money;
		System.out.println
		(Thread.currentThread().getName()+"님이 "+
				money+"원을 출금하여 "+balance+"원이 남았습니다.");
	}
	
}

// 스레드를 구현할 클래스
class User extends Thread {
	
	ATM atm;
	
	public User() { }
	
	public User(ATM atm, String name) {
		super(name);
		this.atm = atm;
	}

	
	@Override
	public void run() {
		this.atm.deposit(20000);  // 입금하는 메서드 호출
 		this.atm.withdraw(30000); // 출금하는 메서드 호출
	}
}

public class ATM_Main {

	public static void main(String[] args) {
		
		ATM atm = new ATM(0);
		
		// User 클래스를 대상으로 멀티스레드 객체 생성
		User user1 = new User(atm, "홍길동");
		User user2 = new User(atm, "이순신");
		User user3 = new User(atm, "유관순");
		User user4 = new User(atm, "김유신");
		User user5 = new User(atm, "김연아");
		
		user1.start(); user2.start();
		user3.start(); user4.start(); user5.start();

	}

}

결과