프로그램에서 명령을 실행하기 위한 최소의 작업 단위 (프로그램 흐름)
프로세스(Process) : 메모리에 저장되어 중앙처리장치(CPU)에 의해 실행되는 명령
Thread 클래스 : 스레드 관련 정보를 저장하고 스레드 관련 기능을 메소드로 제공하는 클래스
Thread.currentThread() : 현재 사용중인 스레드에 대한 Thread 객체를 반환하는 메소드
Thread.getName() : Thread 객체에 저장된 스레드의 이름(식별자)을 반환하는 메소드
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); }}}
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); }}}
명령을 동시에 처리 가능
프로그램 개발자가 스레드를 직접 생성하여 여러 명령을 동시에 실행되도록 만든 프로그램
프로그램의 모든 스레드가 소멸되면 프로그램 종료
GUI 프로그램, Web 프로그램 등은 다중 스레드 프로그램으로 작성
Thread.sleep(long ms) : 현재 명령을 실행하는 스레드를 원하는 시간(ms)만큼 일시적으로 중지하는 메소드
Runnable 인터페이스를 상속받은 자식클래스 작성
→ 자식클래스가 다른 클래스를 상속받아 Thread 클래스를 상속받지 못할 경우 사용하는 방법
Runnable 인터페이스를 상속받은 자식클래스의 run() 메소드를 오버라이드 선언
→ run() 메소드에는 프로그램 개발자에 의해 생성된 스레드가 실행할 명령 작성
Thread 객체 생성 - 생성자 매개변수에 Runnable 인터페이스를 상속받은 자식클래스의 객체를 전달받아 생성
Thread 객체로 start() 메소드 호출
→ Thread 객체로 새로운 스레드를 만들어 Runnable 인터페이스를 상속받은 자식클래스에 오버라이드 선언된
run()메소드에 명령 실행
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); }}}
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(); }}}}
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) 기능 제공
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+"원."); }}
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); }}}
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(); }}