[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("메인 스레드 종료");
}
}
>>> 작업 내용을 저장함.
>>> 작업 내용을 저장함.
>>> 메인 스레드 종료
'데이터 엔지니어링 과정 > java' 카테고리의 다른 글
[24일차] 예외 처리 & 기본 API 클래스 (0) | 2023.01.26 |
---|---|
[23일차] 중첩 클래스와 중첩 인터페이스 (1) | 2023.01.25 |
[22일차] 추상 클래스 & 인터페이스 (0) | 2023.01.20 |
[21일차] 상속 (1) | 2023.01.19 |
[20일차] 메소드 (0) | 2023.01.18 |