반응형
01. 운영체제 & JWM 에 의한 애플리케이션 실행방법
- 운영체제로부터 실행에 필요한 메모리를 할당받아 애플리케이션이 실행된다.
- 자바는 운영체제 위에 JVM이 올라가고, JVM으로부터 메모리를 할당받아 애플리케이션이 실행된다.
하드디스크(보조기억장치)
- 보조기억장치(비휘발성) , 단가쌈
메모리(주기억장치)
- 주기억장치 (휘발성) , flach memory , 단가비쌈
- 요즘 비휘발성에 단가 저렴한 메모리가 생겨남! 즉, 모든 것이 메모리에 저장되어 처리할 수 있을듯?
CPU(중앙처리장치)
- 프로그램 실행 (아이콘 클릭!)
- 보조기억장치에 있는 메모리가 주기억장치(ram)로 이동
- 그것을 대신 해주는 것, 도와주는 것이 운영체제
- 자바는 JVM이 도와줌
02. 프로세스(Process)와 스레드(Thread)
🎩프로세스(Process)
- 메모리에 저장되어 중앙처리장치(CPU)에 의해 실행되는 명령 프로그램
- [1개 프로세스 내 2개 스레드가 있다] = 2개의 코드 실행 흐름을 가진 프로그램
🎩스레드(Thread)
- 프로그램에서 명령을 실행하기 위한 최소의 작업 단위 - 프로그램 흐름
- 하나의 스레드 = 하나의 코드 실행 흐름
- 스레드 = 프로세스를 구성하는 실행의 최소 단위
- 스레드 = 프로세스 내부 코드의 실행 흐름
🎩멀티프로세스(MultiProcess)와 멀티스레드(MultiThread)
- 프로세스1 에는 메모장 앱이 실행중
- 프로세스2에는 계산기 앱이 실행중 , 프로세스3에는 계산기 앱이 실행중
- (즉, 2개의 프로세스 실행 = 계산기 애플리케이션 2개 실행중)
- 프로세스4에는 카카오톡 앱이 실행중
03. 입출금 프로그램 단일스레드
1. 💸 Account.java
package xyz.itwill.thread;
//은행계좌정보(잔액)를 저장하기 위한 클래스
public class Account {
//필드
private int balance;
//생성자
public Account() { }
public Account(int balance) {super(); this.balance = balance;}
//Getter & Setter 메소드
public int getBalance() {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 /*synchronized*/ 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. 🏦 AccountUser.java
package xyz.itwill.thread;
//은행 계좌 사용자 정보(은행계좌정보, 사용자명)를 저장하기 위한 클래스
public class AccountUser /*extends Thread*/ {
//필드
private Account account; //은행계좌정보 - 포함관계
private String userName;
//생성자
public AccountUser() { }
public AccountUser(Account account, String userName) {
super();
this.account = account;
this.userName = userName;
}
//Getter & Setter 메소드
public Account getAccount() {return account;}
public void setAccount(Account account) {this.account = account;}
public String getUserName() {return userName;}
public void setUserName(String userName) {this.userName = userName;}
}
3. 🎩AccountUserApp (단일스레드 입출금 프로그램)
package xyz.itwill.thread;
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[0] = new AccountUser(new Account(10000), "홍길동");과 같은효과
users[1] = new AccountUser(account, "임꺽정");
users[2] = new AccountUser(account, "전우치");
//일괄처리
for(AccountUser user:users) {
user.getAccount().deposit(user.getUserName(), 5000);
}
}
}
//[입금]홍길동님이 5000원을 입금하여 잔액은 15000원 입니다.
//[입금]임꺽정님이 5000원을 입금하여 잔액은 20000원 입니다.
//[입금]전우치님이 5000원을 입금하여 잔액은 25000원 입니다.
04. 입출금 프로그램 다중스레드 (문제점)
1. 💸 Account.java - (변동x)
package xyz.itwill.thread;
//은행계좌정보(잔액)를 저장하기 위한 클래스
public class Account {
//필드
private int balance;
//생성자
public Account() { }
public Account(int balance) {super(); this.balance = balance;}
//Getter & Setter 메소드
public int getBalance() {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 /*synchronized*/ 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. 🏦AccountUser.java - (변동o)
package xyz.itwill.thread;
//은행 계좌 사용자 정보(은행계좌정보, 사용자명)를 저장하기 위한 클래스
public class AccountUser extends Thread {
//필드
private Account account; //은행계좌정보 - 포함관계
private String userName;
//생성자
public AccountUser() { }
public AccountUser(Account account, String userName) {
super();
this.account = account;
this.userName = userName;
}
//Getter & Setter 메소드
public Account getAccount() {return account;}
public void setAccount(Account account) {this.account = account;}
public String getUserName() {return userName;}
public void setUserName(String userName) {this.userName = userName;}
}
@Override
public void run() {
//프로그램 개발자의 의해 생성된 새로운 스레드가 run() 메소드의 명령 실행
// => 은행계좌 사용자에 의한 은행계좌의 입금 처리 메소드 호출
//1.
account.deposit(userName, 5000);
//2.
//account.withDraw(username, 5000);
}
}
3. 🎩AccountUserApp (다중스레드 입출금 프로그램)
public class AccountUserApp {
public static void main(String[] args) {
//은행계좌정보를 생성하여 저장
Account account = new Account(10000); //처음 잔액 : 10000원 (동일한 계좌)
//main스레드의 역할 : new연산자와 스레드만 호출(start()메소드)하고 다음 코드 실행
//다중 스레드를 이용하여 은행계좌 사용자를 생성하여 입금 처리 - 입금처리가 run메소드 안에서 실행됨
new AccountUser(account, "홍길동").start(); //새 스레드(AccountUser스레드1) 형성되어 run메소드 호출됨
//=> AccountUser객체와 스레드1객체 생성 -> 스레드1이 run()메소드 호출 -> 입금처리코드 실행
new AccountUser(account, "임꺽정").start(); //새 스레드(AccountUser스레드2) 형성되어 run메소드 호출됨
//=>AccountUser객체와 스레드2객체 생성 -> 스레드2이 run()메소드 호출 -> 입금처리코드 실행
new AccountUser(account, "전우치").start(); //새 스레드(AccountUser스레드3) 형성되어 run메소드 호출됨
//=>AccountUser객체와 스레드3객체 생성 -> 스레드3이 run()메소드 호출 -> 입금처리코드 실행
//[입금]임꺽정님이 5000원을 입금하여 잔액은 20000원 입니다.
//[입금]홍길동님이 5000원을 입금하여 잔액은 20000원 입니다.
//[입금]전우치님이 5000원을 입금하여 잔액은 25000원 입니다.
// => 이상한 결과값
// => 새로운 스레드들이 자동으로 입금처리했지만, 문제는 어떤 스레드가 먼저 입금처리했을지 모름!
// => 다시말해, 어떤 스레드가 먼저 CPU에게 전달하느냐에 따라서 결과값이 달라짐
// => CPU는 스레드가 요청한 결과값을 요청한 스레드에게 주어야하는데, 요청하지 않은 다른 스레드에게도 무작위로 줄 수 있음
// => 즉, 공유데이터에 대한 결과를 공유하는 스레드라면 모두 공유값을 받을 수 있음!
// => 이것이 바로 다중스레드의 단점!!
}
}
05. 입출금 프로그램 다중스레드 (해결법)
🎩 다중스레드의 문제점
- 동일한 다수의 스레드 (= 같은 클래스로 만들어진 스레드)가 run메소드의 명령을 동시에 실행할 경우,
- 메소드를 호출해 필드값(공유값)을 변경하면
- 잘못된 결과를 발생할 수 있음
- (발생하지 않을수도 있지만 발생할 가능성이 높음)
🎩 다중스레드의 문제 해결법
- 스레드 동기화를 이용하여 스레드에 대한 메소드 호출을 제어할 수 있도록 만들기
🎩스레드 동기화(Thread Synchronized)
- 스레드에 의해 메소드 호출 시 메소드의 명령을 모두 처리하기 전까지 다른 스레드에 호출을 방지하기 위한 기능
- 스레드 락(Lock) 기능 제공
🎩스레드 동기화 처리의 단점
- 실행속도가 매우 느려짐ㅠ
- 그래서 무조건 모든 메소드들을 동기화 처리하는 것은 아님!
🎩그럼 스레드 동기화 처리 언제 사용하면 좋을까?
- 동일한 다수의 스레드 (= 같은 클래스로 만들어진 스레드)가
- run메소드의 명령을 동시에 실행하여 메소드를 호출해 필드값(공유값)을 변경할 때
1. 💸 Account.java - (🐻방법1에 의해 변동o)
package xyz.itwill.thread;
//은행계좌정보(잔액)를 저장하기 위한 클래스
public class Account {
//필드
private int balance;
//생성자
public Account() { }
public Account(int balance) {super(); this.balance = balance;}
//Getter & Setter 메소드
public int getBalance() {return balance;}
public void setBalance(int balance) {this.balance = balance;}
//🐻방법1에 의해 변동o
//입금 처리 메소드 - 매개변수로 입금자와 입금액을 전달받아 처리
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. 🏦 AccountUser.java - (🐻방법2에 의해 변동o)
package xyz.itwill.thread;
//은행계좌 사용자정보(은행계좌정보, 사용자명)를 저장하기 위한 클래스
public class AccountUser extends Thread {
//필드
private Account account; //은행계좌정보 - 포함관계
private String userName;
//생성자
public AccountUser() { }
public AccountUser(Account account, String userName) {
super();
this.account = account;
this.userName = userName;
}
//Getter & Setter 메소드
public Account getAccount() {return account;}
public void setAccount(Account account) {this.account = account;}
public String getUserName() {return userName;}
public void setUserName(String userName) {this.userName = userName;}
}
@Override
public void run() {
//프로그램 개발자의 의해 생성된 새로운 스레드가 run() 메소드의 명령 실행
// => 은행계좌 사용자에 의한 은행계좌의 입금 처리 메소드 호출
//account.deposit(userName, 5000);
//🐻방법2에 의해 변동o
synchronized (account) {
account.withDraw(userName, 5000);
}
}
}
3. 🎩AccountUserApp (다중스레드 입출금 프로그램)
package xyz.itwill.thread;
//다중스레드 프로그램의 문제점
// => 동일한 다수의 스레드가 run() 메소드의 명령을 동시에 실행할 경우 메소드를 호출하여
//필드값(공유)을 변경하면 잘못된 처리결과 발생 가능
//해결법)스레드 동기화를 이용하여 스레드에 대한 메소드 호출 제어
//스레드 동기화(Thread Synchronize) : 스레드에 의해 메소드 호출시 메소드의 모든 명령을 처리
//하기 전까지 다른 스레드의 메소드 실행을 방지하기 위한 기능
// => 스레드를 일시 중지하여 명령이 실행되지 않도록 락(Lock)기능 제공
//스레드 동기화 처리 방법
//🐻방법1 - Account.java
// => 동기화 메소드 만들기(Synchronized Method) - 권장
// => 클래스 내 메소드에 직접 작성
// => Synchronized 키워드를 사용하여 메소드 선언
// => 동기화메소드를 이용할 때마다 스레드 락(Lock) 기능 제공
// => 동기화를 선언한 메소드만 스레드 락 기능이 제공됨
// => 단, 어떤 스레드가 먼저 접근할지는 아무도 모르지만, 먼저 접근한 스레드의 코드가 다 실행될 동안 다른 스레드 호출 차단!
// => 형식) 접근제한자 synchronized 반환형 메소드명(자료형 매개변수명,...) { }
//🐻방법2 - AccountUser.java
// => Synchronized 키워드로 블록 설정 후 메소드 호출 하기 - 비권장
// => 클래스 내 메소드를 직접 고칠 수 없다면??????
// => ex) 배포된클래스 : 상속받은 스레드 클래스에 작성 - 비권장.. 왜? - 어떤 메소드는 동기화처리해야하지만, 어떤 메소드는 동기화처리하면 안되기 때문
// => 객체로 호출되는 모든 메소드는 동기화 처리되어 실행됨
//형식) synchronized(객체) { 객체.메소드명(값,...); ... } - 객체로 호출되는 모든 메소드는 동기화 처리되어 실행
public class AccountUserApp {
public static void main(String[] args) {
//은행계좌정보를 생성하여 저장
Account account=new Account(10000);//잔액 : 10000원
//다중 스레드를 이용하여 은행계좌 사용자를 생성하여 입금(출금) 처리
//🐻방법1 - Account.java 변경으로 인한 결과값
//1. 스레드 동기화 처리한 후 결과값 (입금)
new AccountUser(account, "홍길동").start();
new AccountUser(account, "임꺽정").start();
new AccountUser(account, "전우치").start();
//[입금]홍길동님이 5000원을 입금하여 잔액은 15000원 입니다.
//[입금]전우치님이 5000원을 입금하여 잔액은 20000원 입니다.
//[입금]임꺽정님이 5000원을 입금하여 잔액은 25000원 입니다.
//정상결과
//2. 스레드 동기화 처리한 후 결과값 (출금)
new AccountUser(account, "홍길동").start();
new AccountUser(account, "임꺽정").start();
new AccountUser(account, "전우치").start();
//[출금]홍길동님이 5000원을 출금하여 잔액은 5000원 입니다.
//[출금]전우치님이 5000원을 출금하여 잔액은 0원 입니다.
//[에러]임꺽정님, 잔액이 0원 남아 5000원을 출금할 수 없습니다.
//정상결과
}
//🐻방법2 - AccountUser.java 변경으로 인한 결과값
//배포받은 클래스는 직접 고칠 수 없어!! - 어떻게 할까? 2번째 방법을 이용하자!
new AccountUser(account, "홍길동").start();
new AccountUser(account, "임꺽정").start();
new AccountUser(account, "전우치").start();
//[출금]홍길동님이 5000원을 출금하여 잔액은 5000원 입니다.
//[출금]전우치님이 5000원을 출금하여 잔액은 0원 입니다.
//[에러]임꺽정님, 잔액이 0원 남아 5000원을 출금할 수 없습니다.
//정상결과
}
}
}
06. 배포된 클래스의 대다수는 동기화 처리 되어있다??
- ex) StringBuffer클래스는 동기화 처리된 메소드들을 가지고 있어 다중스레드 프로그램 만들 때 안전하게 조작가능
- 반면에 StringBuilder클래스는 비동기화 처리 메소드라 안전하지 않음
- 대신 처리속도가 훨씬 빠름
- 다중스레드를 사용해야 하는데, 이로 인해 잘못된 결과가 나올 수 있을 것 같다면 안전하게 StringBuffer이용하기
- 이 밖에도 동기화처리된 메소드들 많으니 살펴보기
반응형
'java > java.lang' 카테고리의 다른 글
[java/lang] 7. Thread 클래스 (1) | 2024.06.16 |
---|---|
[java/lang] 6. Exception 클래스 (0) | 2024.06.16 |
[java/lang] 5. StringBuffer 클래스 (0) | 2024.06.15 |
[java/lang] 4. Wrapper 클래스 (1) | 2024.06.15 |
[java/lang] 3. String 클래스 (0) | 2024.06.14 |