1-1. 직렬화(Serialization)
- 객체를 통째로 파일 쓰기(저장) / 읽기, 객체를 통째로 네트워크를 통해 송신(쓰기) / 수신(읽기) 이것을 가능하게 해주는 것이 '직렬화' 이다.
- 객체 단위로 입출력을 하기 위해서 직렬화를 한다.
데이터 전송 --> 직렬화 --> 역직렬화
- 직렬화 ? 객체 -> 데이터 스트림 변환하는 작업
- 역직렬화 ? 데이터 스트림 -> 객체 변환하는 작업
직렬화하는 예제1) 한 멤버의 정보를 파일로 저장 후 읽기
[Member 클래스]
class Member {
// field
private String name;
private int age;
private boolean gender;
private long type;
// constructor
public Member() { }
public Member(String name, int age, boolean gender, long type) {
super();
this.name = name;
this.age = age;
this.gender = gender;
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public boolean isGender() {
return gender;
}
public void setGender(boolean gender) {
this.gender = gender;
}
public long getType() {
return type;
}
public void setType(long type) {
this.type = type;
}
@Override
public String toString() {
return String.format("%s,%d,%b,%d", name, age, gender, type);
}
} // Member class
[main 메서드] : 파일 저장

[main 메서드] : 파일 읽기

직렬화하는 예제2) 한 멤버의 정보를 파일로 저장 후 읽기 - 메서드 선언
[Member 클래스 안 메서드 - 파일 저장]

[Member 클래스 안 메서드 - 파일 읽기]

[main() 메서드]

위의 예제 2가지처럼 객체를 직렬화하고, 역직렬화하는 코딩을 몰라도 된다.
ObjectInputStream과 ObjectOutputStream 스트림 클래스가 존재하여, 사용하는 방법만 알면 된다!
1-2. 직렬화 - ObjectInputStream과 ObjectOutputStream 스트림 클래스
- 직렬화에는 ObjectOutputStream 사용
- 역직렬화에는 ObjectInputStream 사용
- ObjectInputStream과 ObjectOutputStream은 각각 InputStream과 OutputStream을 상속받지만, 기반 스트림을 필요로 하는 보조 스트림이다. -> 객체를 생성할 때 입출력(직렬화/역직렬화)할 스트림을 지정해주어야 함!
[ObjectInputStream과 ObjectOutputStream 메서드]

1-3. 직렬화 - Serializable, transient(직렬화 가능한 클래스 만들기)
1) 직렬화가 가능한 클래스를 만드는 2가지 방법이 있다.
(1) Serializable 인터페이스를 구현해야 된다. (implements Serializable)
(2) Externalizable 인터페이스를 구현 후 writeExternal()와 readExternal() 메서드 직접 구현
2) transient 제어자 : 직렬화 대상에서 제외시키는 제어자
Serializable, Externalizable, transient 사용 예제)
- 1. 부모가 직렬화가 가능한 클래스로 선언이 되면, 자식은 자동으로 직렬화가 가능한 클래스가 된다.
- 2. 부모의 멤버는 직렬화 대상에서 제외가 되고, 자식 멤버만 직렬화가 가능하다.

[main() 메서드]

[Person 클래스]

[Member 클래스] : tansient 제어자 사용

[Member 클래스 전체 코드]
class Member implements Serializable{
// 시리얼 버전 UID
private static final long serialVersionUID = 2336313051081995427L;
// field
private String name;
private int age;
private boolean gender;
private transient long type; // 직렬화 제외(객체 -> 스트림)
// constructor
public Member() { }
public Member(String name, int age, boolean gender, long type) {
super();
this.name = name;
this.age = age;
this.gender = gender;
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public boolean isGender() {
return gender;
}
public void setGender(boolean gender) {
this.gender = gender;
}
public long getType() {
return type;
}
public void setType(long type) {
this.type = type;
}
@Override
public String toString() {
return String.format("%s,%d,%b,%d", name, age, gender, type);
}
} // Member class
- 3. 직렬화 대상에 제외된 부모의 멤버도 직렬화 포함시키고 싶다면... -> 직접 메서드를 구현해야 한다.
[main 메서드]

[Person 클래스와 Member 클래스]

[직렬화 다시 한번 정리!]

이제.. 대망의... th레드.....ㅎ

2. 쓰레드(thread) == 스레드
1) 프로세스(process)
- 실행 중인 프로그램(작업관리자에서 확인 가능)
- OS(운영체제)로부터 실행에 필요한 자원(메모리, CPU, 전력, 디스크 등)을 할당받아서 실행 중인 프로그램을 '프로세스'라고 한다.
ex) calc.exe 실행파일 -> 실행 -> 실행중인 프로그램 == 프로세스1
calc.exe 실행파일 -> 실행 -> 실행 중인 프로그램 == 프로세스2
2) 쓰레드(thread)
- 프로세스 내에서 독립적으로 실행되는 메서드(기능)
> 작업관리자 - 성능 - 리소스모니터 - CPU 탭 - 쓰레드 확인 가능
- 프로세스 자원을 이용해서 실제로 작업을 수행하는 것 == 쓰레드
- 최소한 1개 이상의 쓰레드 존재 == main 쓰레드
- 여러 개의 쓰레드가 존재할 수 있다 == 멀티 쓰레드
3) 멀티쓰레드와 멀티태스킹
- 멀티쓰레드 : 여러 개의 쓰레드 --> OS가 자동으로 관리
- 멀티태스킹 : 여러 개의 프로세스
- 싱글쓰레드로 작성되어 있다면 파일을 다운로드하는 동안에 다른 일을 전혀 할 수 없다. (ex. 음악 들으면서 이클립스로 코딩하기..)
- 멀티쓰레딩 : 하나의 프로세스 내에서 여러 쓰레드가 동시에 작업을 수행하는 것
[장점]
ㄱ. CPU의 사용률을 향상시킨다.
ㄴ. 자원을 보다 효율적으로 사용할 수 있다
ㄷ. 사용자에 대한 응답성이 향상된다.
ㄹ. 작업이 분리되어 코드가 간결해진다.
[단점]
ㄱ. 여러 쓰레드가 자원을 공유하면서 작업을 하기 때문에 동기화, 교착상태와 같은 문제 발생
-> 해결방안은 다시 배울 예정


4) CPU(중앙처리장치)는 시분할(시간을 분할)해서 멀티 태스킹을 하고 있다. --> OS가 자동으로 관리
- 시분할을 하는데 속도가 너무 빨라서 우리 눈에는 동시에 처리하는 것처럼 보여진다.
- 멀티태스킹(멀티쓰레드)은 'OS'가 자동으로 관리하기 때문에 개발자가 쓰레드를 처리하기 어렵다.
5) 자바에서 쓰레드를 사용하는 방법(구현) 2가지
(1) Runnable 인터페이스를 구현한 클래스 생성
- 다른 클래스를 상속 받지 않아도 되면 인테페이스로 구현
(2) Thread 클래스 사용
- 다른 클래스를 상속받아야 할 때 사용
[쓰레드 예제]
싱글쓰레드 사용 예제)

- 쓰레드는 OS가 자동으로 관리하는 것이라 방청소를 먼저 할지, 장보기를 먼저 할지 순서를 알 수는 없음
- 단, 쓰레드가 1개이기 때문에 하나의 작업이 다 끝나야 그다음 작업을 할 수 있음

장보기 일을 하는 쓰레드를 선언하고 run() 메서드와 start() 메서드 사용)

- target.run() -> 일을 할수는 있지만, 독립적으로 일을 하지 않는다. 즉, main 쓰레드 에서 run()을 호출하는 것은 생성된 쓰레드(ShoppingWorker)를 실행시키는 것이 아니라 단순히 클래스에 선언된 메서드를 호출시키는 것이다.
- 그래서 main 쓰레드가 run() 메서드를 통해서 장보기 일을 다 하고 END를 찍은 뒤 ShoppingWorker가 장보기 일을 시작한다. (쓰레드 선언 및 생성 코드가 target.run() 메서드 다음에 있기 때문에)
- 반면, ShoppingWorker 쓰레드 객체를 생성하여 start() 메서드를 호출하면 독립적인 작업을 수행할 수 있는 새로운 쓰레드를 생성한 다음에 코딩을 한 순서에 맞게 작업이 실행이 된다.
- 주어진 시간동안 작업을 마치지 못한 쓰레드는 다시 자신의 차례가 돌아올 때까지 대기상태로 있게되며, 작업을 마친 쓰레드는 사라지게된다. -> 이 부분은 후반부에 배웠어서 정리하면서 밑에서 다시 다룰 것!

main 쓰레드와 청소하는 쓰레드와 장보기하는 쓰레드가 일하는 과정)
[청소하는 쓰레드와 장보기하는 쓰레드]

[main 메서드]
public static void main(String[] args) {
// main 스레드(스레드는 기본적으로 1개 존재한다) == 독릭접으로 일하는 일꾼
Thread t = Thread.currentThread(); // 현재 실행되고 있는 스레드를 가져오는 메서드
String tName = t.getName(); // 스레드의 이름을 가져오는 메서드
System.out.println(tName); // main
// main 스레드보다 먼저 시키기 위해서 코딩을 위로 올림 - 독립적으로 스레드(일꾼)을 만들어서 장보기를 시키자
Runnable target = new ShoppingWorker(); // Runnable로 선언해도 된다. 인터페이스를 구현했기 때문에
// ShoppingWorker target = new ShoppingWorker();
// target.run(); // 일을 할수는 있지만 독립적으로 일을 하지 않음, main 스레드의 일이 끝나야 실행되는듯?
Thread 장보기일꾼 = new Thread(target);
장보기일꾼.setName("shopping");
장보기일꾼.start(); // start() 메서드를 선언하면 내부적으로 ShoppingWorker 클래스 안에 있는 run() 이라는 메서드를 호출되어진다.
// 1) 방청소 - 내부적으로 main 스레드가 실행 중
for (int i = 1; i <= 100; i++) {
System.out.printf("> %s - 방청소 : %d%%\n", tName, i);
} // for i
// 1) 방청소 - 방청소일꾼이 청소중
Thread 방청소일꾼 = new CleaningWorker("cleaning");
방청소일꾼.setName("cleaning");
방청소일꾼.start();
// 2) 마트에서 장보기 - main 스레드(일꾼)은 1개이니까 방청소가 끝나고 마트에서 장을 보러가야함
for (int i = 1; i <= 100; i++) {
System.out.printf("> %s - 장보기 : %d%%\n", tName, i);
} // for i
/*
// 2) 마트에서 장보기 - 독립적으로 스레드(일꾼)을 만들어서 장보기를 시키자
Runnable target = new ShoppingWorker(); // Runnable로 선언해도 된다. 인터페이스를 구현했기 때문에
// ShoppingWorker target = new ShoppingWorker();
// target.run(); // 일을 할수는 있지만 독립적으로 일을 하지 않음, main 스레드의 일이 끝나야 실행되는듯?
Thread 장보기일꾼 = new Thread(target);
장보기일꾼.setName("shopping");
장보기일꾼.start(); // start() 메서드를 선언하면 내부적으로 ShoppingWorker 클래스 안에 있는 run() 이라는 메서드를 호출되어진다.
*/
System.out.println("END");
} // main - main 스레드가 종료된다 == 실행중인 프로그램 종료 == 프로세스 종료
} // class
- main() 메서드를 실행시켜보면 콘솔창에서 결과가 아래와 같이 나온다.




결과를 보면 알겠지만, main 쓰레드가 청소를 하다가 갑자기 장보기 쓰레드가 shopping을 하다가 다시 메인쓰레드가 청소를 하다가 방청소 쓰레드가 cleaning 작업을 하다가 장보기 쓰레드가 shopping을 하다가... 이런식으로 순서가 뒤죽박죽인걸 볼 수 있다. 순서가 뒤죽박죽인 것은 OS가 자동으로 관리하고 있기 때문이다.
실제로 실행시킬 수 있는 쓰레드는 1개이고 나머지 쓰레드는 대기상태에 있다가 주어진 시간이 지나면 다른 쓰레드가 실행이 되는 과정을 반복하다 모든 쓰레드들이 작업을 다 마치게 되고 쓰레드들은 사라지게된다.
main 쓰레드로 일을 시키고 파일복사일꾼 쓰레드로 파일복사하기)
[main() 메서드]

[파일복사일꾼 클래스]
class FileCopy implements Runnable {
// 필드
private String originalPath;
private String copyPath;
// 생성자
public FileCopy(String originalPath, String copyPath) {
this.originalPath = originalPath;
this.copyPath = copyPath;
}
@Override
public void run() {
// 파일 복사(다운로드)라는 코딩..
long start = System.nanoTime();
File orignalFile = new File(originalPath);
System.out.println(orignalFile.length() + " bytes"); // 2,385,496 bytes
FileInputStream fis = null;
FileOutputStream fos = null;
//
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
//
final int BUFFER_SIZE = 1024;
byte[] buffer = new byte[BUFFER_SIZE];
try {
fis = new FileInputStream(orignalFile);// File
fos = new FileOutputStream(copyPath); // String
// 보조스트림 사용
bis = new BufferedInputStream(fis, BUFFER_SIZE);
bos = new BufferedOutputStream(fos, BUFFER_SIZE);
int readNumber = -1; // 실제 읽어온 바이트 수를 저장할 변수
while ((readNumber = bis.read(buffer)) != -1) {
bos.write(buffer, 0, readNumber);
} // while
bos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try { // try-with-resource 안에 넣지 않아서 다 닫아줘야함
bis.close();
bos.close();
fis.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
} // try
long end = System.nanoTime();
System.out.println("> 처리 시간 : " + (end - start) + "ns");
} // run
} // FileCopy
main 쓰레드로 일을 시키고 파일복사일꾼 쓰레드로 파일복사하기 + Frame 클래스 상속받아서 버튼을 만들기)
package days29;
import java.awt.Button;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Ex05_02 {
public static void main(String[] args) {
// 음악 다운로드 - 멜론
// main 스레드(기본 스레드) - main 스레드는 일을 시키고 끝남
String originalPath = "C:\\Program Files (x86)\\CBT\\CBT.exe";
String copyPath = "C:\\Users\\Yelin Park\\Documents\\CBT.exe";
// 일은 파일복사일꾼이 한다.
FileCopy target = new FileCopy(originalPath, copyPath);
System.out.println("END"); // main 스레드는 일을 시키고 끝남
} // main
} // class
class FileCopy extends Frame implements Runnable {
// 필드
private String originalPath;
private String copyPath;
Button btnFileCopy; // 파일 복사하는 버튼
// 생성자
public FileCopy(String originalPath, String copyPath) {
this.originalPath = originalPath;
this.copyPath = copyPath;
//
this.setTitle(this.originalPath + "파일 복사 중...");
this.setSize(500, 100);
this.setVisible(true);
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.out.println("> 프로그램 종료!! <");
System.exit(-1);
}
});
Thread 파일복사일꾼 = new Thread(this);
파일복사일꾼.setName("fcopy");
this.btnFileCopy = new Button("File Copy");
this.btnFileCopy.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// 스레드 시작
System.out.println("> 파일 복사 버튼을 클릭했습니다. 파일 복사 시작합니다 <");
파일복사일꾼.start();
}
} );
this.add(btnFileCopy);
} // 생성자 닫기
@Override
public void run() {
// 파일 복사(다운로드)라는 코딩..
long start = System.nanoTime();
File orignalFile = new File(originalPath);
System.out.println(orignalFile.length() + " bytes"); // 2,385,496 bytes
FileInputStream fis = null;
FileOutputStream fos = null;
//
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
//
final int BUFFER_SIZE = 1024;
byte[] buffer = new byte[BUFFER_SIZE];
try {
fis = new FileInputStream(orignalFile);// File
fos = new FileOutputStream(copyPath); // String
// 보조스트림 사용
bis = new BufferedInputStream(fis, BUFFER_SIZE);
bos = new BufferedOutputStream(fos, BUFFER_SIZE);
int readNumber = -1; // 실제 읽어온 바이트 수를 저장할 변수
while ((readNumber = bis.read(buffer)) != -1) {
bos.write(buffer, 0, readNumber);
} // while
bos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try { // try-with-resource 안에 넣지 않아서 다 닫아줘야함
bis.close();
bos.close();
fis.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
} // try
long end = System.nanoTime();
System.out.println("> 처리 시간 : " + (end - start) + "ns");
} // run
} // FileCopy
멀티쓰레드 예제)
[main() 메서드]

[ForThread 클래스 - for문을 돌리는 쓰레드]

[InputThread 클래스 - 입력용 대화상자를 띄우고 입력받아 출력하는 쓰레드]

> 아래 사진처럼 대화상자는 떠있지만 콘솔창에서는 for문이 계속 돌아가는 것을 확인할 수 있다.

아래 콘솔창을 보면 대화상자에 값을 입력하고 엔터를 치면 dd라고 콘솔창에 출력이 되는데 아직도 for문이 돌아가고 있다. t1과 t2 쓰레드가 각각 독립적으로 일을 하고 있기 때문이다.
for문이 돌아가고 있는 상태에서도 t1 쓰레드로 인해 대화상자에 값을 입력하고 엔터를 치면 t1의 작업은 대화상자를 띄우고 값을 입력받아서 출력하는 것이기 때문에 값을 출력하고 t2의 쓰레드는 사라진다. t1은 아직 20번을 돌지 않았기 때문에 계속 작업을 하고 있는 것이다.

2-1. 쓰레드의 우선순위(priority)
- 멀티 스레드 : OS 자동 관리( 자원(CPU) 할당 )
- 멀티 스레드 중에 우선순위를 설정할 수 있다. : setPriority() 메서드로 설정이 가능, 기본값은 5
- 우선순위는 10이 제일 높고 1이 제일 낮다. ( 1 ~ 10)
- 10(우선권 제일 높은 값) 의미 == 특정 스레드가 더 많은 작업 시간을 갖도록 하는 설정
출처 : 자바의 정석
쓰레드의 우선순위 예제)
[main() 메서드]

[PriorityWorker 클래스]

<결과>

위의 결과와 같이.. 개발자가 우선권을 주더라도 OS가 관리하기 때문에 우선권이 높은 스레드가 먼저 끝나지는 않을 수 있다. 그럼에도 불구하고 특정 스레드가 더 많은 작업 시간을 갖도록하여 빨리 끝날 수 있도록 하는 것..!
2-2. 쓰레드의 실행제어
[쓰레드의 메서드]

오늘은 sleep(), interrupt() 2가지만 배웠고 JDBC 수업하면서 나머지 메서드는 다시 배울 예정이다.
단, suspend(), resume(), stop() 이 3가지 메서드는 deprecated 되어있어 사용을 권장하지 않아 배우지 않는다.
[쓰레드의 상태]



쓰레드 상태 살펴보는 예제)
[SatusWorker 클래스 - 쓰레드의 상태를 알아보기 위한 쓰레드]

[main() 메서드]

<결과>

sleep() 메서드 설명 : 쓰레드를 일시정지 시키는 메서드

interrupt()와 interrupted() 메서드 설명 : 쓰레드의 작업을 취소하는 메서드
- interrupt() 메서드 : 쓰레드에게 작업을 멈추라고 '요청'한다. 단지, 멈추라고 요청만 하는 것일뿐 쓰레드를 강제로 종료시키지는 못한다. interrupted 상태만을 false -> true 변경하는 일만 한다.
- interrupted() 메서드 : 쓰레드에 대해 interrupt() 메서드가 호출되었는지 알려준다. interrupt() 메서드가 호출되면 true, 호출되지 않았다면 false를 반환한다.
interrupt()와 interrupted() 설명 예제1)
[main() 메서드]

[ThreadWorker 클래스]

interrupt()와 interrupted() 설명 예제2)
[main() 메서드]

[ThreadWorker02 클래스 interrupt() 사용 전]

분명 main 메서드 안에서 t.interrupt() 코딩을 했는데 try-catch문의 catch를 만나 예외를 발생시키고는 while문이 계속 돌아가고 있다. 그 이유는 interrupt() 메서드를 호출하면, 예외가 발생되고 쓰레드의 interrupted 상태가 false로 자동 초기화가 되기 때문에 while 문으로 다시 돌아갔을 때 !isInterrupted() 값이 true가 되기 때문에 계속해서 출력을 하고 있는 것이다.
계속 출력되는 문제를 해결하기 위해서는 catch문 안에 interrupt()메서드를 호출해주면 초기화된 상태를 true로 바꿔서 쓰레드 작업을 중지시킬 수 있다.
[ThreadWorker02 클래스 interrupt() 사용 후]

sleep() 메서드와 interrupt() 메서드 사용 및 설명 예제)
> 쓰레드 상태 살펴보는 예제 코딩에 아래 코딩을 추가하였다.

[전체 코드]
public class Ex08 {
public static void main(String[] args) {
// sleep() - RUNNABLE상태 -> 일시정지상태(TIMED_WAITING) -> n초후 자동으로 실행대기상태
// wait() - RUNNABLE상태 -> WAITING -> notify() / notifyAll() -> RUNNABLE상태
SatusWorker t1 = new SatusWorker();
// 1. t1 스레드 생성했지만, t1.start() 메서드는 호출하지 않은 상태
System.out.println(t1.getState()); // NEW
// 2. 실행대기상태
t1.start();
System.out.println(t1.getState()); // RUNNABLE
/*
- t1 바로 실행된다면 1, 2 값이 찍혀야 하는데 main 스레드가 t1의 상태를 알려주고 main이 끝난다음에 t1이 일을함
RUNNABLE
1.....
2.....
*/
// 3. 4.
try {
Thread.sleep(3000); // main 쓰레드 n초동안 일시정지 상태된다. n초 후에 자동으로 실행대기 상태가 된다.
} catch (InterruptedException e) {
e.printStackTrace();
} // try
// 3. t1.run() 처리 후 처리가 완료되면, 자동으로 스레드 종료 -> TERMINATED
// 4. t1이 5초동안 sleep() 일시정지 상태, main 스레드가 3초 일시정지 후 TIMED_WAITING 상태를 찍어주고 2 값을 찍음
System.out.println(t1.getState());
// Ex09 관련 - 아직 t1 sleep(5000) 일시정지된 상태일 때
// interrupt() 메서드로 호출해서 예외를 발생시키고 실행대기 상태가 된다.
t1.interrupt();
// System.out.println(t1.getState());
System.out.println("main END");
} // main
} // class
class SatusWorker extends Thread{
@Override
public void run() {
System.out.println("1.....");
// 3번 설명하실 때 추가함
try {
this.sleep(5000); // t1 스레드가 5초동안 일시정지
} catch (InterruptedException e) {
// e.printStackTrace();
// Ex09 관련
System.out.println(">>>>> InterruptedException 예외 발생");
} // try
System.out.println("2.....");
} // run
} // SatusWorker
<결과>

- sleep() 메서드로 인해 쓰레드가 일시정지상태(TIMED_WAITING)에 있을 때, 해당 쓰레드에 대해 interrupt() 메서드를 호출하면, InterruptedException이 발생하고 쓰레드는 실행대기상태(RUNNABLE)로 바뀐다. 즉, 작업을 멈추고있던 쓰레드를 깨워서 실행가능한 상태로 만드는 것이다.
(1) main() 메서드 안에 Thread.sleep(3000); 코딩으로 main 쓰레드가 3초동안 일시정지 상태되고, 3초 후에 자동으로 실행대기상태(RUNNABLE)가 된다.
(2) 3초후 System.out.println(t1.getState()); 코딩을 만나서 t1의 상태를 찍어준다.
(3) t1도 메서드 안에서 this.sleep(5000); 코딩으로 인해 5초동안 일시정지가 되어있는 상태라 (TIMED_WAITING)를 출력해준다.
(4) main 쓰레드는 작업이 다 끝나 END를 출력하고,
(5) 일시정지상태인 t1은 t1.interrupt(); 코딩으로 인해 예외를 발생시키고 실행대기상태(RUNNABLE)가 된다.
(6) SatusWorker 클래스 안에 catch문으로 인하여 System.out.println(">>>>> InterruptedException 예외 발생"); 코딩을 실행시키고,
(7) 2..... 값을 콘솔창에 출력한다.(남은 작업)
(8) 예외가 발생한 뒤 쓰레드의 interrupted 상태는 false로 자동 초기화된다.
멀티스레드 [문제점만 파악] -> 해결은 동기화 처리 배울 때 다시!
- 하나의 스레드가 작업을 마무리할 때까지 다른 스레드가 공유자원을 사용하지 못하도록 하는 작업을 [동기화처리]라고 한다.
- 들어오지 말아야하는 공유자원 영역을 '임계영역'이라고 한다. -> 들어오지 못하게 잠금을 한다. == 동기화처리

[main() 메서드]

[PrintWorker 클래스 - 인쇄하는 쓰레드]

[PrintMachine 클래스]

<결과>

- 여러 개의 쓰레드(멀티쓰레드)가 공유된 자원인 프린트기를 같이 사용하다보니 인쇄된 결과물이 섞여서 나왔다.
'TIL > Java' 카테고리의 다른 글
[Java] 문자열을 XML 파일로 생성하기 + DOM 객체를 파일로 저장하는 방법(DomSource, TransformerFactory, Transformer) (0) | 2022.10.31 |
---|---|
[Java] StringUtils 클래스 (0) | 2022.10.04 |
[SIST] Java_days28 (0) | 2022.03.29 |
[SIST] Java_days27 (0) | 2022.03.28 |
[SIST] Java_days26 (0) | 2022.03.25 |