스레드

woom·2022년 11월 20일
0

JAVA

목록 보기
13/18
post-thumbnail

🌼 스레드(Thread)

  • 프로그램에서 명령을 실행하기 위한 최소의 작업 단위 (프로그램 흐름)

  • 프로세스(Process) : 메모리에 저장되어 중앙처리장치(CPU)에 의해 실행되는 명령

  • Thread 클래스 : 스레드 관련 정보를 저장하고 스레드 관련 기능을 메소드로 제공하는 클래스

  • Thread.currentThread() : 현재 사용중인 스레드에 대한 Thread 객체를 반환하는 메소드

  • Thread.getName() : Thread 객체에 저장된 스레드의 이름(식별자)을 반환하는 메소드


📙 1. 단일 스레드 프로그램

  • 명령을 하나씩 처리
  • JVM에 의해 생성된 main 스레드를 이용하여 main() 메소드의 명령 실행
  • main() 메소드가 종료되면 main 스레드는 자동 소멸 → 프로그램 종료

🐣 예제1

public class SingleThread {
	public void display() {
		
		System.out.println("SingleThread 클래스의 display() 메소드 시작");
		
		System.out.println("["+Thread.currentThread().getName()
				+"] 스레드에 의해 display() 메소드의 명령 실행");
		
		System.out.println("SingleThread 클래스의 display() 메소드 종료");
		
		
		for(char i='A';i<='Z';i++) {
			System.out.print(i); }}}
		




🐣 예제2 (실행)

public class SingleThreadApp {
	public static void main(String[] args) {
		
		System.out.println("SingleThreadApp 클래스의 main() 메소드 시작");
		//Thread.currentThread() : 현재 사용중인 스레드에 대한 Thread 객체를 반환
		//Thread.getName() : Thread 객체에 저장된 스레드의 이름(식별자)을 반환
		System.out.println("["+Thread.currentThread().getName()
				+"] 스레드에 의해 main() 메소드의 명령 실행");
		
		//객체를 사용하여 메소드를 호출한 경우 스레드가 클래스의 메소드로 이동하여 명령 실행
		// → 메소드의 명령을 모두 실행한 뒤 다시 되돌아와 나머지 명령 실행
		new SingleThread().display();//객체를 생성한 후 메소드 호출
		
		System.out.println("SingleThreadApp 클래스의 main() 메소드 종료");
		
		
		for(char i='0';i<='9';i++) {
			System.out.print(i); }}}





📙 2. 다중 스레드 프로그램

  • 명령을 동시에 처리 가능

  • 프로그램 개발자가 스레드를 직접 생성하여 여러 명령을 동시에 실행되도록 만든 프로그램

  • 프로그램의 모든 스레드가 소멸되면 프로그램 종료

  • GUI 프로그램, Web 프로그램 등은 다중 스레드 프로그램으로 작성

  • Thread.sleep(long ms) : 현재 명령을 실행하는 스레드를 원하는 시간(ms)만큼 일시적으로 중지하는 메소드

    • InterruptedException 발생 : 일반 예외 (예외처리를 하지 않으면 에러 발생)

💡 프로그램 개발자가 main 메소드 명령이 아닌 새로운 스레드를 생성하여 명령을 실행하는 방법1

  1. Thread 클래스를 상속받은 자식클래스 작성
  2. Thread 클래스를 상속받은 자식클래스의 run() 메소드를 오버라이드 선언
    → run() 메소드에는 프로그램 개발자에 의해 생성된 스레드가 실행할 명령 작성
  3. Thread 클래스를 상속받은 자식클래스로 객체 생성 (Thread 객체 생성)
  4. 자식클래스의 객체로 start() 메소드 호출 (Thread 객체의 start() 메소드 호출)
    → Thread 객체로 새로운 스레드를 만들어 자식클래스에 오버라이드 선언된 run()메소드의 명령 실행
    → Thread 객체로 start() 메소드 외 다른 메소드를 호출하지 않을 경우 참조변수를 사용하지 않고 직접 start() 메소드 호출

💡 프로그램 개발자가 main 메소드 명령이 아닌 새로운 스레드를 생성하여 명령을 실행하는 방법2

  1. Runnable 인터페이스를 상속받은 자식클래스 작성
    → 자식클래스가 다른 클래스를 상속받아 Thread 클래스를 상속받지 못할 경우 사용하는 방법

  2. Runnable 인터페이스를 상속받은 자식클래스의 run() 메소드를 오버라이드 선언
    → run() 메소드에는 프로그램 개발자에 의해 생성된 스레드가 실행할 명령 작성

  3. Thread 객체 생성 - 생성자 매개변수에 Runnable 인터페이스를 상속받은 자식클래스의 객체를 전달받아 생성

  4. Thread 객체로 start() 메소드 호출
    → Thread 객체로 새로운 스레드를 만들어 Runnable 인터페이스를 상속받은 자식클래스에 오버라이드 선언된
    run()메소드에 명령 실행


🐣 예제1 (실행)

  • Thread(Runnable target) : Runnable 인터페이스를 상속받은 자식클래스의 객체를 전달 받아 Thread 객체 생성

public class MultiThreadApp {
	//main() 메소드에 의해 전달되는 모든 예외는 JVM에 의해 자동으로 예외 처리
	public static void main(String[] args) throws InterruptedException {
		//JVM에 의해 main 스레드가 생성되어 main() 메소드의 명령 실행
		
		
		//Thread 객체를 생성하여 start() 메소드 호출
		//새로운 스레드가 만들어지면 Thread 클래스의 run() 메소드를 호출하여 명령 실행
		//Thread thread=new Thread();
		//thread.start(); : (Thread 클래스의 run() 메소드에는 명령 미존재)
		
		
		MultiThreadOne one=new MultiThreadOne();
		one.start();
		
		
		//Thread 객체로 start() 메소드외 다른 메소드를 호출하지 않을 경우 참조변수를
        //사용하지 않고 객체를 생성하여 직접 start() 메소드 호출
		//새로운 스레드가 생성되어 MultiThreadOne 클래스의 run() 메소드의 명령 실행
		new MultiThreadOne().start();	
		//하나의 Thread 객체는 start() 메소드로 하나의 스레드만 생성하여 명령 실행 가능

		//Thread(Runnable target) : Runnable 인터페이스를 상속받은 자식클래스의
        //객체를 전달 받아 Thread 객체 생성
		new Thread(new MultiThreadTwo()).start();
		
		for(char i='0';i<='9';i++) {
			System.out.print(i);
			
			//Thread.sleep(long ms) : 현재 명령의 스레드를 원하는 시간(ms)만큼 중지
			Thread.sleep(500);	}}}





🐣 예제2 (방법1)


public class MultiThreadOne extends Thread {
	@Override
	public void run() {
		for(char i='A';i<='Z';i++) {
			System.out.print(i);
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace(); }}}}




🐣 예제3 (방법2)


public class MultiThreadTwo implements Runnable {
	@Override
	public void run() {
		for(char i='a';i<='z';i++) {
			System.out.print(i);
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace(); }}}}
                




📌 다중스레드 프로그램의 문제점

  • (같은 클래스로 만들어진) 동일한 다수의 스레드가 run() 메소드의 명령을 동시에 실행할 경우 메소드를 호출하여 필드값(공유)을 변경하면 잘못된 처리결과 발생 가능
    즉, 어떤 스레드가 먼저 처리(실행)될지 알수없음(불확실성)

  • 해결법 : 스레드 동기화를 이용하여 스레드에 대한 메소드 호출 제어


📕 스레드 동기화

  • Thread Synchronize : 스레드에 의해 메소드 호출시 메소드의 모든 명령을 처리하기 전까지 다른 스레드의 메소드 실행을 방지하기 위한 기능

  • 스레드를 일시 중지하여 명령이 실행되지 않도록 락(Lock) 기능 제공

  • 스레드 동기화 처리 방법
  1. synchronized 키워드를 사용하여 메소드 선언
    • 동기화 메소드(Syncronized Method)
    • 형식) 접근제한자 synchronized 반환형 메소드명 (자료형 매개변수명, ... ) { }
  2. synchronized 키워드로 블럭을 설정하여 메소드 호출
    • 형식) synchronized ( 객체 ) { 객체, 메소드명 ( 값, ... ); ... }
    • 객체로 호출되는 모든 메소드는 동기화 처리되어 실행

🐣 예제1 (잔액 저장)

  • 은행계좌정보(잔액)를 저장하기 위한 클래스
public class Account {
	private int balance;//balance(잔액) 필드 선언
	
	public Account() { }//기본생성자

	public Account(int balance) {//생성자 초기화
		super();
		this.balance = balance;
	}
	public int getBalance() {//getter, setter 메소드 선언
		return balance;
	}
	public void setBalance(int balance) {
		this.balance = balance;
	}
	//입금 처리 메소드 - 매개변수로 입금자와 입금액을 전달받아 처리
	public synchronized void deposit(String name, int amount) {	
		balance+=amount;
		System.out.println(name+"님이 "+amount+"원을 입금. 잔액은"+balance+"원.");
	}
	//출금 처리 메소드 - 매개변수로 출금자와 출금액을 전달받아 처리
	public void withDraw(String name, int amount) {
		if(balance<amount) {
			System.out.println(name+"님 잔액"+balance+"원 남아"+amount+"원 출금불가.");
			return;
		}
		balance-=amount;
		System.out.println(name+"님이 "+amount+"원을 출금. 잔액은 "+balance+"원."); }}




🐣예제2 (다중스레드)

  • 은행계좌 사용자정보(은행계좌정보, 사용자명)를 저장하기 위한 클래스
public class AccountUser extends Thread {//Thread 클래스 상속
	private Account account;//Account 클래스의 account필드 선언
	private String userName;
	
	public AccountUser() { }//기본생성자

	public AccountUser(Account account, String userName) {//생성자 초기화
		super();
		this.account = account;
		this.userName = userName;
	}
	public Account getAccount() {//getter, setter 메소드 선언
		return account;
	}
	public void setAccount(Account account) {
		this.account = account;
	}
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	@Override	//run()메소드 오버라이드
	public void run() {
		//프로그램 개발자에 의해 생성된 새로운 스레드가 run() 메소드의 명령 실행
		// → 은행계좌 사용자에 의한 은행계좌의 입금 처리 메소드 호출
		account.deposit(userName, 5000);//account클래스의 deposit메소드 호출
		
		synchronized (account) {
			account.withDraw(userName, 5000); }}}
            
            
            
            

🐣예제3 (동기화실행)

public class AccountUserApp {
	public static void main(String[] args) {
		//은행계좌정보를 생성하여 저장
		Account account=new Account(10000);		//잔액 : 10000원
		
		//단일 스레드(main)를 이용하여 은행계좌 사용자를 생성하여 입금 처리
		AccountUser[] users=new AccountUser[3];
		//모든 사용자가 동일한 계좌 사용
		users[0]=new AccountUser(account, "홍길동");
		users[1]=new AccountUser(account, "임꺽정");
		users[2]=new AccountUser(account, "전우치");
		
		for(AccountUser user:users) {
			user.getAccount().deposit(user.getUserName(), 5000);
		}
		
		
		//다중 스레드를 이용하여 은행계좌 사용자를 생성하여 입금(출금) 처리
		new AccountUser(account, "홍길동").start();
        //start 메소드 호출 → run메소드 실행
		new AccountUser(account, "임꺽정").start();
		new AccountUser(account, "전우치").start(); }}





profile
Study Log 📂

0개의 댓글

관련 채용 정보