Java Thread 동기화, 입출력

EUNLEE·2022년 7월 15일
0

Thread


  1. 상속
    • Thread class
  2. Runnable interface를 구현한 class를 작성
    • instance를 생성
    • 이 객체를 이용해서 Thread 생성

Thread의 생명주기

join()

Thread 자신이 하던 작업을 잠시 멈추고 다른 Thread가 지정된 시간동안 작업을 수행하도록 할 때 사용한다.

  • join()
  • join(time)

code로 알아보아요 ~

package lecture0715;

class ThreadEx_07_1 extends Thread {
	@Override
	public void run() {
		for(int i=0; i<300; i++) {
			System.out.print("-");
		}
	}
}

class ThreadEx_07_2 extends Thread {
	@Override
	public void run() {
		for(int i=0; i<300; i++) {
			System.out.print("|");
		}
	}
}

public class ThreadExam07 {
	
	public static void main(String[] args) {
		Thread t1 = new ThreadEx_07_1();
		Thread t2 = new ThreadEx_07_2();
	
		t1.start();
		t2.start();
		
		try {
			t1.join();
			t2.join();
		} catch (Exception e) {
			// TODO: handle exception
		}
		System.out.println("<<main>> 종료 ");
	}
}
package lecture0715;

class ThreadEx_08_1 extends Thread {
	
	final static int MAX_MEMORY = 1000; // 내가 사용할 수 있는 메모리의 총량
	int usedMemory = 0;
	
	@Override
	public void run() {
		while(true) {
			try {
				Thread.sleep(10000); // 10초 동안 자요!
			} catch (Exception e) {
				System.out.println("interrupt()에 의해서 깨어났어요!");
			}
			gc();
			System.out.println("메모리 청소 완료! 현재 사용 메모리 량 : " + freeMemory() );
		}
	}
	
	public void gc() {
		usedMemory -= 300;
		if(usedMemory < 0) {
			usedMemory = 0;
		}
	}
	
	public int totalMemory() { return MAX_MEMORY; }
	public int freeMemory() { return MAX_MEMORY - usedMemory; }
}

public class ThreadExam08 {

	public static void main(String[] args) {
		ThreadEx_08_1 t = new ThreadEx_08_1();
		t.setDaemon(true);
		t.start();
		
		int requiredMemory = 0;
		
		for(int i=0; i<20; i++) {
			requiredMemory = ((int)(Math.random() * 10)) * 20; // 0.0보다 같거나 크고 10보다 작은 정수
			
			// 필요한 memory가 사용할 수 있는 양보다 크거나
			// 현재 전체 메모리양의 60% 이상을 사용하고 있을 때 gc를 실행
			if((requiredMemory > t.freeMemory()) || (t.freeMemory() < t.totalMemory() * 0.4)) {
				t.interrupt(); // gc()실행이 끝날때까지 기다리지 않아요! 
				try {
					t.join();
				} catch (Exception e) {
					// TODO: handle exception
				}
			}
			t.usedMemory += requiredMemory;
			System.out.println("사용된 메모리 량 : " +t.usedMemory);
		}
	}
}

Thread의 동기화

Java에서 “Lock”을 얻어 임계영역을 설정하려면

synchronized keyword

  • method 동기화
  • 🌟 동기화 block을 생성
    • method의 코드가 길다면 동기화 block을 사용하는 것이 더 효율적이다.
package lecture0715;

// Thread에 의해서 공유되는 공유객체를 생성하기 위한 class
class Account {
	
	private int balance = 1000; // 계좌 잔액
	
	public int getBalance() {
		return balance;
	}
	
	// 출금하는 method
	public void withdraw(int money) {
		if(balance >= money) {
			try {
				**Thread.sleep(1000)**; // 하나의 thread가 자고 있는 동안 다른 thread가 들어오게 된다.
			} catch (Exception e) {
				// TODO: handle exception
			}
			balance -= money;
		}
	}
}

class ThreadEx_09 implements Runnable {
	
	Account acc = new Account(); // 공용 객체 
	
	@Override
	public void run() {
		while(acc.getBalance() > 0) {
			int money = ((int) (Math.random() * 3 + 1) * 100);
			acc.withdraw(money);
			System.out.println("남은 잔액은 : " + acc.getBalance());
		}
		
	}
}

public class ThreadExam09 {

		public static void main(String[] args) {
			ThreadEx_09 r = new ThreadEx_09(); // runnable 객체
			
			Thread t1 = new Thread(r);
			Thread t2 = new Thread(r);
			
			t1.start();
			t2.start();
		}
}
/* 출력 */
남은 잔액은 : 900
남은 잔액은 : 900
남은 잔액은 : 600
남은 잔액은 : 500
남은 잔액은 : 200
남은 잔액은 : 200
남은 잔액은 : 0
남은 잔액은 : -200 

// 실행시켜보면 출력이 따닥따닥 떨어지는 것이 보인다. -> 두개의 thread가 동시에 실행
// 하나의 thread가 자고 있는 동안 다른 thread가 들어와서 수행되므로 잔액이 음수가 찍히게 된다.

해결법 1. method 동기화

package lecture0715;

// Thread에 의해서 공유되는 공유객체를 생성하기 위한 class
class Account {
	
	private int balance = 1000; // 계좌 잔액
	
	public int getBalance() {
		return balance;
	}
	
	// 출금하는 method => 동기화 처리 
	public **synchronized** void withdraw(int money) { // 먼저 들어온 thread가 lock을 얻게된다. -> thread가 순차처리됨.
		if(balance >= money) {
			try {
				Thread.sleep(1000);
			} catch (Exception e) {
				// TODO: handle exception
			}
			balance -= money;
		}
	}
}

class ThreadEx_09 implements Runnable {
	
	Account acc = new Account(); // 공용 객체 
	
	@Override
	public void run() {
		while(acc.getBalance() > 0) {
			int money = ((int) (Math.random() * 3 + 1) * 100);
			acc.withdraw(money);
			System.out.println("남은 잔액은 : " + acc.getBalance());
		}
		
	}
}

public class ThreadExam09 {

		public static void main(String[] args) {
			ThreadEx_09 r = new ThreadEx_09(); // runnable 객체
			
			Thread t1 = new Thread(r);
			Thread t2 = new Thread(r);
			
			t1.start();
			t2.start();
		}
}
/* 출력 */
남은 잔액은 : 800
남은 잔액은 : 500
남은 잔액은 : 200
남은 잔액은 : 200
남은 잔액은 : 200
남은 잔액은 : 100
남은 잔액은 : 100
남은 잔액은 : 0
남은 잔액은 : 0
// 실행시켜보면 하나씩 딱딱 떨어지는 것을 알 수 있다. -> thread가 순차적으로 실행

해결법2. 동기화 block을 생성 🌟 

package lecture0715;

// Thread에 의해서 공유되는 공유객체를 생성하기 위한 class
class Account {
	
	private int balance = 1000; // 계좌 잔액
	
	public int getBalance() {
		return balance;
	}
	
	// 출금하는 method => 동기화 처리 
	public void withdraw(int money) {
		
		**// 동기화 블럭** 
		**synchronized (this) {
			if(balance >= money) {
				try {
					Thread.sleep(1000);
				} catch (Exception e) {
					// TODO: handle exception
				}
				balance -= money;
			}**
		}
	}
}

class ThreadEx_09 implements Runnable {
	
	Account acc = new Account(); // 공용 객체 
	
	@Override
	public void run() {
		while(acc.getBalance() > 0) {
			int money = ((int) (Math.random() * 3 + 1) * 100);
			acc.withdraw(money);
			System.out.println("남은 잔액은 : " + acc.getBalance());
		}
		
	}
}

public class ThreadExam09 {

		public static void main(String[] args) {
			ThreadEx_09 r = new ThreadEx_09(); // runnable 객체
			
			Thread t1 = new Thread(r);
			Thread t2 = new Thread(r);
			
			t1.start();
			t2.start();
		}
}
/* 출력 */
남은 잔액은 : 900
남은 잔액은 : 700
남은 잔액은 : 400
남은 잔액은 : 300
남은 잔액은 : 0
남은 잔액은 : 0

syncronized를 이용해서 공유데이터를 보존한다. Thread가 공유자원에 대한 LOCK을 획득한 후 오랜 시간을 보낸다!

→ 문제가 발생한다.

  • wait() → LOCK을 놓고 대기
  • notify() → 대기상태에 있는 Thread가 실행할 수 있도록 block을 해제

동기화 keyword

연습문제 풀어보기
1. “1초마다 자신의 이름을 출력하는 Thread를 2개 생성” 이름을 출력할 때 교대로 출력하는걸 보장하는 코드를 작성해보아요!
2. Thread가 3개 이상일 때 순서대로 출력
3. [1번 2번 출력] → 1초 뒤 → [1번 2번 출력] → 1초 뒤 → [1번 2번 출력] → 1초 뒤 → .. 나오게 하기

Java 입출력


  • IO(Input/output)
    • 쉽다.
  • NIO(New Input/output)
    • IO의 개량
    • Java4, Java7 정착
    • 어렵다.

Java IO

  • Stream이라는 객체를 이용해서 입/출력을 처리
  • java.io package로 제공

표준입력 → keyboard

  • system.in

표준출력 → monitor

  • system.out

🌟 Stream instance

Java에서 특정 장치에서 data를 읽거나 특정장치로 data를 보낼 때 사용하는 매개 객체

→Java program과 File/monitor/keyboard/.. 간의 중간 다리 역할(통로)을 한다.

Stream 객체의 특징

  1. 단방향

    • 내보내는 통로와 받는 통로가 따로 있다고 생각!
    • Stream을 생성할 때 Stream의 종류와 방향이 결정
      • InputStream
      • OutputStream
  2. FIFO 구조

  1. 결합이 가능
    • 사용하기 편한 stream을 만들어서 사용

Stream 구분

  • Input/output
  • Byte/문자Stream

Object Stream을 통해서 객체(instance)도 전달할 수 있어요.

단, 모든 객체가 다 되는건 아니에요!!

⇒ 만약 instance를 생성한 class가 Serializable interface를 구현하고 있으면 가능!!

0개의 댓글