Thread

프로세스

 : 프로세스프로그램에 대한 각각의 인스턴스를 의미하므로, 같은 프로그램을 여러 개 띄웠다고 해서 하나의 프로세스를 공유하는 것은 아니다.

 : 프로세스는 운영체제로부터 주소공간, 파일, 메모리 등을 할당받는다.

 : 간단히 말하면 실행중인 프로그램

 : 프로그램을 수행하는데 필요한 데이터, 메모리, 자원, 쓰레드로 구성되어 있다.

쓰레드

 : 프로세스의 자원으로 실제 작업을 실행하는 것이 쓰레드이다.

 : 한 프로세스 내에서 동작되는 여러 실행의 흐름으로, 프로세스 내의 주소 공간이나 자원들을 대부분 공유하면서 실행된다.


프로세스와 쓰레드

 > 프로세스는 자신만의 고유 공간과 자원을 할당 받아 사용하는데 비해 스레드는 다른 스레드와 공간과 자원을 공유하여 사용한다.


Thread 사용의 장점

 : 스레드는 프로세스내에서 각각의 스택 공간을 제외한 나머지 공간과 시스템 자원을 공유한다.
 : 그러므로 프로세스를 이용하여 동시에 처리하던 일을 스레드로 구현할 경우 메모리 공간은 물론 시스템 자원 소모도 현격히 줄어든다.

 : 스레드 간의 통신이 필요할 경우 별도의 자원을 이용하는 것이 아니라 전역 변수의 공간을 이용하여 데이터를 주고 받을 수 있다.


 - 시스템의 처리량이 향상된다.

 - 시스템의 자원 소모가 줄어든다.

 - 프로그램의 응답 시간이 단축된다.

 - 프로세스 간 통신 방법에 비해 스레드 간의 통신 방법이 훨씬 간단하다.

 ( 스레드 간의 통신 시 데이터를 주고받는 방법은 메모리 공간을 공유하므로 데이터 세그먼트, 즉 전역 변수를 이용하여 구현한다. )

 (공유하는 전역 변수를 여러 스레드가 함께 사용하기 위해서는 충돌 문제가 발생할 수 있는데 이런 문제가 발생하지 않도록 동기화 문제를 해결해야 한다.)


Thread 사용의 단점

 : 여러 개의 스레드를 이용하는 프로그램을 작성하는 경우 주의 깊게 설계해야 한다. 

 : 미묘한 시간 차나 잘못된 변수를 공유함으로써 오류가 발생할 수 있다.



자바에서의 Thread

 1. 쓰레드 클래스 상속하기 (다른 클래스를 상속받을 수 없다.)


public class ThreadEx extends Thread {


@Override

public void run() { // Thread 코드


}


public static void main(String[] args) {

new ThreadEx().start(); // 쓰레드 시작 .start();


}

}


 2. Runnable 인터페이스 구현하기 (재사용성이 높으며, 일관성 유지가능)


public class ThreadEx implements Runnable {


@Override

public void run() { // Thread 코드


}


public static void main(String[] args) {

ThreadEx tEx = new ThreadEx();

Thread t = new Thread(tEx); // 쓰레드 구현

t.start();

}

}


 3. 쓰레드의 특징

  - start() vs run()

  - start()

 : 새로운 스레드가 작업을 실행하는데 필요한 호출스택을 생성한 후 run()을 호출 해 새로 생성한 호출스택 첫번째로 run()을 저장한다.

  - run()

 : 스레드를 실행시키는게 아니라 클래스에 속한 메서드 하나를 호출하는 것


 ※ 모든 쓰레드는 독립적인 작업 수행을 위해 자신만의 호출 스택을 필요로 하기 때문에 

새로운 쓰레드를 생성하고 실행시킬 때마다 새로운 호출스택이 생성되고 쓰레드가 종료되면 호출 스택은 소멸된다.


run() 메소드 호출 시 Main() 위에 Run() 메소드를 하나 호출하는 것 

start()할 때 독립적인 작업 수행을 위해

쓰레드마다 각자의 콜 스택을 생성

run() 메소드를 실행한다.


※ 4개의 호출스택(Thread)가 번갈아 가며 실행하는

 멀티쓰레드(다중처리처럼 보이는 것)


 4. Thread의 동기화(synchronized)

 : 싱글스레드의 경우 프로세스 내 단 하나의 스레드만 작업하기 때문에 자원을 이용하는 데에 문제가 없지만 멀티 스레드일 경우 같은 자원을 공유할 때 문제가 생길 수 있다.

 : 그렇기 때문에 현재 작업 시 lock을 걸어 다른 스레드가 접근하지 못하게 하는 작업이 필요하다. 이 작업을 '동기화(synchronized)'라고 한다.


 - 하나의 객체에 다수의 Thread가 접근 시

  Synchronized(객체 참조변수) { }

 - 하나의 메서드 내에서 객체 참조를 하는 경우

  public Synchronized void 메소드명( ) { }



MultiThread Example Code




public class Multithread {


SharedArea area; // 공유 객체 선언


public Multithread() {

area = new SharedArea(); // 공유 객체 생성

area.account1 = new Account("111-111-1111", "이몽룡", 20000000);

area.account2 = new Account("222-222-2222", "성춘향", 10000000);

TransferThread thread1 = new TransferThread(area); // 입/출금 Thread 객체

thread1.start();

PrintThread thread2 = new PrintThread(area); // 결과 출력 Thread 객체

thread2.start();

}


public static void main(String[] args) {

new Multithread();

}

}


/**

 *

 * @author SeokRae

 * @Description 출력 Thread

 */

class PrintThread extends Thread {

SharedArea sharedArea;


public PrintThread(SharedArea area) {

sharedArea = area;

}


@Override

public void run() {

for (int cnt = 0; cnt < 12; cnt++) {

int sum = sharedArea.getTotal();

System.out.println("계좌 잔액 합계 : " + sum);

try {

Thread.sleep(1); // 두 Thread의 실행 시간을 맞추기 위해 1/1000 초간 시간을 지연시킨다.

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}


/**

 * 

 * @author SeokRae

 * @description 입/출금

 */

class TransferThread extends Thread {

SharedArea sharedArea; // 공유 객체 선언


public TransferThread(SharedArea area) {

this.sharedArea = area;

}


@Override

public void run() {

for (int cnt = 0; cnt < 12; cnt++) {

sharedArea.transfer(100); // 계좌 이체 메소드 호출

try {

Thread.sleep(1); // 두 Thread의 실행 시간을 맞추기 위해 1/1000 초간 시간을 지연시킨다.

} catch (InterruptedException e) {

e.printStackTrace();

}

}


}

}


/**

 * 

 * @author SeokRae

 * @description 계좌정보

 */

class Account {

String accountNo; // 계좌번호

String ownerName; // 예금주 이름

int balance; // 잔액


// shift + art + s > o

// 필드 값을 모두 사용하는 생성자


public Account(String accountNo, String ownerName, int balance) {

this.accountNo = accountNo;

this.ownerName = ownerName;

this.balance = balance;

}


void deposit(int amount) { // 입금

balance += amount; // 예금액을 잔액에 추가

}


int withdraw(int amount) { // 출금

if (balance < amount) { // 잔액값보다 출금액이 클 경우

return 0;

}

balance -= amount; // 잔액에서 출금액을 뺀다.

return amount;

}

}


/**

 * 

 * @author SeokRae

 * @description 공유 객체

 * @function 계좌 입/출력, 합계

 */

class SharedArea {

Account account1;

Account account2;


synchronized void transfer(int amount) {

// synchronized(this){

account1.withdraw(amount * 10000);

System.out.print("이몽룡 계좌 : " + amount + "만원 인출 ,");

account2.deposit(amount * 10000);

System.out.println("성춘향 계좌 : " + amount + "만원 입금");

// }

}


synchronized int getTotal() { // 잔액의 합계를 구한다.

return account1.balance + account2.balance;

}


}




+ Recent posts