[25일차] 기본 API & 스레드

2023. 1. 27. 16:07데이터 엔지니어링 과정/java

목차
1. java.util 패키지
2. 멀티 스레드
3. 스레드 제어

1. java.util 패키지

1. Date 클래스

  • 특정 시점의 날짜를 표현하는 클래스
  • SimpleDateFormat 클래스
    • 원하는 날짜 형식의 문자열을 얻고 싶다면 함께 사용
    • format() 메소드를 호출해서 원하는 형식의 날짜 정보 얻음
package sec02.exam01;

import java.text.SimpleDateFormat;
import java.util.Date;

public class DateExample {
	public static void main(String[] args) {
		Date now = new Date();
		String strNow1 = now.toString();
		System.out.println(strNow1);
		
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy년 MM월 dd일 hh시 mm분 ss초");
		String strNow2 = sdf.format(now);
		System.out.println(strNow2);
	}
}

>>> Fri Jan 27 09:47:49 KST 2023
>>> 2023년 01월 27일 09시 47분 49초

2. Calendar 클래스

  • 달력을 표현한 클래스
  • 추상 클래스 이므로 new 연산자 사용하여 인스턴스 생성 불가
  • getInstance() 메소드 이용하여 현재 운영체제에 설정되어 있는 시간대를 기준으로 한 Calendar 하위 객체 얻음
    • get() 메소드를 이용해서 날짜와 시간에 대한 정보 얻음
package sec02.exam02;

import java.util.Calendar;

public class CalendarExample {
	public static void main(String[] args) {
		Calendar now = Calendar.getInstance();
		
		int year = now.get(Calendar.YEAR);
		int month = now.get(Calendar.MONTH);
		int day = now.get(Calendar.DAY_OF_MONTH);
		
		int week = now.get(Calendar.DAY_OF_WEEK);
		String strWeek = null;
		switch(week) {
			case Calendar.MONDAY:
				strWeek = "월";
				break;
			case Calendar.THURSDAY:
				strWeek = "화";
				break;	
			case Calendar.WEDNESDAY:
				strWeek = "수";
				break;
			case Calendar.TUESDAY:
				strWeek = "목";
				break;
			case Calendar.FRIDAY:
				strWeek = "금";
				break;
			case Calendar.SATURDAY:
				strWeek = "토";
				break;
			default:
				strWeek = "일";
		}
		
		int amPm = now.get(Calendar.AM_PM);
		String strAmPm = null;
		if (amPm == Calendar.AM) {
			strAmPm = "오전";
		} else {
			strAmPm = "오후";
		}
		
		int hour = now.get(Calendar.HOUR);
		int minute = now.get(Calendar.MINUTE);
		int second = now.get(Calendar.SECOND);
		
		System.out.print(year + "년 ");
		System.out.print(month + "월 ");
		System.out.println(day + "일");
		System.out.print(strWeek + "요일 ");
		System.out.println(strAmPm + "   ");
		System.out.print(hour + "시 ");
		System.out.print(minute + "분 ");
		System.out.println(second + "초");
	}
    
>>> 2023년 0월 27일
>>> 금요일 오전   
>>> 10시 19분 12초

 

2. 멀티 스레드

0. 시작하기 전에

  • 프로세스
    : 애플리케이션을 실행하면 운영체제로부터 실행에 필요한 메모리를 할당받아 애플리케이션의 코드를 실행하는 것
  • 스레드
    : 프로세스 내부에서 코드의 실행 흐름

1. 스레드

  • 사전적 의미로 한 가닥의 실
    ➡ 한 가지 작업을 실행하기 위해 순차적으로 실행할 코드를 실처럼 이어놓았다
  • 멀티 스레드 
    : 하나의 프로세스 내부에 생성되었기 때문에, 하나의 스레드가 예외가 발생시키면 프로세스 자체가 종료 될 수 있어 다른 스레드에 영향을 미침

2. 메인 스레드

  • 필요에 따라 작업 스레드들을 만들어 병렬로 코드 실행 가능
  • 싱글 스레드 애플리케이션 ➡ 메인 스레드가 종료되면 프로세스 종료
  • 멀티 스레드 애플리케이션 ➡ 실행 중인 메소드가 하나라도 있다면, 프로세스 종료 불가

3. 작업 스레드 생성과 실행

  • 객체로 생성되기 때문에 클래스 필요
  • Thread 클래스로부터 직접 생성
    • Runnable을 매개값으로 갖는 생성자 호출
    • #보충설명 필요
  • 💻 메인 스레드만 이용하는 경우
package sec01.exam01;

import java.awt.Toolkit;

public class BeepPrintExample {
	public static void main(String[] args) {
		Toolkit toolkit = Toolkit.getDefaultToolkit();
		for (int i=0; i<5; i++) {
			toolkit.beep();
			try {Thread.sleep(500); } catch(Exception e) {}
		}

		for (int i=0; i<5; i++) {
			System.out.println("띵");
			try {Thread.sleep(500); } catch (Exception e) {}
		}
	}
}

>>> 띵
>>> 띵
>>> 띵
>>> 띵
>>> 띵
  • 💻 비프음을 들려주는 작업 정의
package sec01.exam02;

import java.awt.Toolkit;

public class BeepTask implements Runnable {
	public void run() {
		Toolkit toolkit = Toolkit.getDefaultToolkit();
		for (int i=0; i<5; i++) {
			toolkit.beep();
			try {Thread.sleep(500); } catch(Exception e) {}
		}
	}
}
  • 💻 메인 스레드와 작업 스레드 동시 실행
package sec01.exam02;

public class BeepPrintExample2 {
	public static void main(String[] args) {
		Runnable beepTask = new BeepTask();
		Thread thread = new Thread(beepTask);
		thread.start();
		
		for (int i=0; i<5; i++) {
			System.out.println("띵");
			try {Thread.sleep(500); } catch (Exception e) {}
		}
	}
}

>>> 띵
>>> 띵
>>> 띵
>>> 띵
>>> 띵
  • Thread 하위 클래스로부터 생성
    • 작업 스레드 클래스 정의
      ➡ Thread 클래스를 상속한 후 run() 메소드를 재정의해서 스레드가 실행할 코드 작성
  • 💻 Thread 익명 자식 객체 이용하기
package sec01.exam05;

import java.awt.Toolkit;

public class BeepPrintExample5 {
	public static void main(String[] args) {
		Thread thread = new Thread() {
			@Override
			public void run() {
				Toolkit toolkit = Toolkit.getDefaultToolkit();
				for (int i=0; i<5; i++) {
					toolkit.beep();
					try {Thread.sleep(500);} catch (Exception e) {}
				}
			}
		};
		thread.start();
		
		for (int i=0; i<5; i++) {
			System.out.println("띵");
			try {Thread.sleep(500); } catch(Exception e) {}
		}
	}
}

>>> 띵
>>> 띵
>>> 띵
>>> 띵
>>> 띵
  • 스레드의 이름
    • 메인 스레드는 'main'이라는 이름 가짐
    • 직접 생성한 스레드는 자동적으로 'Thread-n'이라는 이름 가짐
      • 이름 변경하고 싶다면 setName() 메소드 사용
      • 이름 알고 싶을 땐 getName()
  • 💻 메인 스레드 이름 출력 및 UserThread 생성 및 시작
package sec01.exam06;

public class ThreadA extends Thread{
	public ThreadA() {
		setName ("THreadA");
	}
	
	public void run() {
		for (int i=0; i<2; i++) {
			System.out.println(getName() + "가 출력한 내용");
		}
	}
}
package sec01.exam06;

public class ThreadB extends Thread {
	public void run() {
		for (int i=0; i<2; i++) {
			System.out.println(getName() + "가 출력한 내용");
		}
	}
}
package sec01.exam06;

public class ThreadNameExample {
	public static void main(String[] args) {
		Thread mainThread = Thread.currentThread();
		System.out.println("프로그램 시작 스레드 이름: " + mainThread.getName());
		
		ThreadA threadA = new ThreadA();
		System.out.println("작업 스레드 이름: " + threadA.getName());
		threadA.start();
		
		ThreadB threadB = new ThreadB();
		System.out.println("작업 스레드 이름: " + threadB.getName());
		threadB.start();
	}
}

4. 동기화 메소드

  • 공유 객체를 사용할 때의 주의할 점
    • 멀티 스레드 프로그램에서 스레드들이 객체를 공유해서 작업해야 하는 경우,
      스레드 A가 사용하던 객체를 스레드 B가 상태 변경할 수 있기 때문에 스레드 A가 의도했던 것과 다른 결과를 산출할 수 있음
  • 💻 메인 스레드가 직접 실행하는 코드
package sec01.exam07;

public class MainThreadExample {
	public static void main(String[] args) {
		Calculator calculator = new Calculator();
		
		User1 user1 = new User1();
		user1.setCalculator(calculator);
		user1.start();
		
		User2 user2 = new User2();
		user2.setCalculator(calculator);
		user2.start();
	}
}

>>> User2: 50
>>> User1: 50
  • 💻 공유 객체
package sec01.exam07;

public class Calculator {
	private int memory;
	
	public int getMemory() {
		return memory;
	}
	
	public void setMemory(int memory) {
		this.memory = memory;
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {}
		System.out.println(Thread.currentThread().getName() + ": " + this.memory);
	}
}
  • 💻 User1 스레드
package sec01.exam07;

public class User1 extends Thread{
	private Calculator calculator;
	
	public void setCalculator(Calculator calculator) {
		this.setName("User1");
		this.calculator = calculator;
	}
	
	public void run() {
		calculator.setMemory(100);
	}
}
  • 💻 User2 스레드
package sec01.exam07;

public class User2 extends Thread{
	private Calculator calculator;
	
	public void setCalculator(Calculator calculator) {
		this.setName("User2");
		this.calculator = calculator;
	}
	
	public void run() {
		calculator.setMemory(50);
	}
}
  • 동기화 메소드
    • 임계 영역 : 멀티 스레드 프로그램에서 단 하나의 스레드만 실행 할 수 있는 코드 영역
      • synchronized : 임계 영역 지정하기 위해 동기화 메소드 제공
package sec01.exam08;

public class Calculator {
	private int memory;
	
	public int getMemory() {
		return memory;
	}
	
	public synchronized void setMemory(int memory) {
		this.memory = memory;
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {}
		System.out.println(Thread.currentThread().getName() + ": " + this.memory);
	}
}

>>> User2: 50
>>> User1: 50

 

2. 스레드 제어

1. 스레드 상태

  • 실행 대기 상태 : 실행을 기다리고 있는 상태
  • 실행 상태 : 운영체제는 하나의 스레드를 선택하고 CPU가 run() 메소드를 실행하도록 함
  • 종료 상태 : 실행 상태에서 run() 메소드가 종료되었을 때, 더 이상 실행할 코드가 없음

2. 스레드 상태 제어

  • 스레드 상태 제어 : 실행 중인 스레드의 상태 변경
  • 주어진 시간 동안 일시 정지 sleep()
  • 💻 3초 주기로 비프 음 10번 발생
package sec02.exam01;

import java.awt.Toolkit;

public class SleepExample {
	public static void main(String[] args) {
		Toolkit toolkit = Toolkit.getDefaultToolkit();
		for (int i=0; i<10; i++) {
			toolkit.beep();
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) { }
		}
	}
}
  • 스레드의 안전한 종료 stop()
  • 💻 무한 반복해서 출력하는 스레드
package sec02.exam02;

public class PrintThread1 extends Thread {
	private boolean stop;
	
	public void setStop(boolean stop) {
		this.stop = stop;
	}
	
	public void run() {
		while(!stop) {
			System.out.println("실행 중");
		}
		System.out.println("자원 정리");
		System.out.println("실행 종료");
	}
}
  • 💻 1초 후 출력 스레드를 중지
package sec02.exam02;

public class StopFlagExample {
	public static void main(String[] args) {
		PrintThread1 printThread = new PrintThread1();
		printThread.start();
		
		try {Thread.sleep(1000);} catch (InterruptedException e) {}
		
		printThread.setStop(true);
	}
}

>>> 실행 중
>>> 실행 중
>>> 실행 중
>>> 실행 중
>>> 자원 정리
>>> 실행 종료
  • interrupt() 메소드 사용
  • 💻 무한 반복해서 출력하는 스레드`
package sec02.exam03;

public class PrintThread2 extends Thread {
	public void run() {
		try {
			while(true) {
				System.out.println("실행 중");
				Thread.sleep(1);
			}
		}catch (InterruptedException e) {}
		
		System.out.println("자원 정리");
		System.out.println("실행 종료");
	}
}
  • 💻 1초 후 출력 스레드를 중지
package sec02.exam03;

public class InterruptExample {
	public static void main(String[] args) {
		Thread thread = new PrintThread2();
		thread.start();
		
		try {Thread.sleep(1000); } catch (InterruptedException e) {}
		
		thread.interrupt();
	}
}

>>> 실행 중
>>> 실행 중
>>> 실행 중
>>> 실행 중
>>> 실행 중
>>> 실행 중
>>> 자원 정리
>>> 실행 종료
  • 💻 무한 반복해서 출력하는 스레드
package sec02.exam04;

public class PrintThread2 extends Thread{
	public void run() {
		while(true) {
			System.out.println("실행 중");
			if (Thread.interrupted()) {
				break;
			}
		}
		System.out.println("자원 정리");
		System.out.println("실행 종료");
	}
}

3. 데몬 스레드

  • 스레드의 작업을 돕는 보조적인 역할을 수행하는 스레드
  • 주 스레드가 종료되면 데몬 스레드는 강제 자동 종료
  • ex) 워드프로세서의 자동 저장, 미디어 프렐이어의 동영상 및 음악 재생, 쓰레기 수집기 등
  • 주 스레드가 데몬이 될 스레드의 setDaemon(true)를  호출하면 됨
  • 💻 1초 주기로 save() 메소드를 호출하는 데몬 스레드
package sec02.exam05;

public class AutoSaveThread extends Thread{
	public void save() {
		System.out.println("작업 내용을 저장함.");
	}
	
	@Override
	public void run() {
		while(true) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				break;
			}
			save();
		}
	}
}
  • 💻 메인 스레드가 실행하는 코드
package sec02.exam05;

public class DaemonExample {
	public static void main(String[] args) {
		AutoSaveThread autoSaveThead = new AutoSaveThread();
		autoSaveThead.setDaemon(true);
		autoSaveThead.start();
		
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
		}
		System.out.println("메인 스레드 종료");
	}
}

>>> 작업 내용을 저장함.
>>> 작업 내용을 저장함.
>>> 메인 스레드 종료