Java Stream, Thread
Java의 Thread 기본 개념과 동시성 해결, Stream의 종류 및 객체 입출력 방식에 대해 설명합니다.
<2019년 7월 9일>
[Thread]
- 프로세스 : 일련의 작업의 단위
- 쓰레드 : 프로세스 내에 기생하는 작업 일부의 단위
- 자바에서는 Thread 클래스를 상속받거나, Runnable 인터페이스를 구현하는 방식으로 쓰레드를 구현할 수 있다. 주로 Runnable 인터페이스를 구현한다.
- Runnable 인터페이서에서는 run() 을 오버라이드에서 실행하고, Thread 클래스에서는 start() 를 오버라이드해서 쓰레드를 구현한다.
- synchronized 를 통해 주어진 변수에 대한 잠금장치(?) 를 설정할 수 있다. (method 에 synchronize 를 설정하는 것이 나을 것인가? 아니면 매소든 내의 블록을 synchronized 로 설정하는게 나을 것인지는 판단해야 한다.) -> 동시성 해결
- 스레드 제어 메서드를 잘 이용하는 것이 중요하다. run(), sleep() ...
* 쓰레드의 life cycle
쓰레드 객체 생성 -> start() -> Runnable(실행 대기) ->
실행 대기 상태에서, run() 이 콜백되게 된다. CPU 는 타임 슬라이스 방식으로 각 쓰레드마다 일정 시간만을 할당해 준다. 시간이 끝나게 되면 다시 작업 대기열에 들어가 있다가 선택받으면 업무를 수행한다.
sleep(), join(), wait(), notify(), notifyAll(), interrupt() 중지
package com.sinc.thread;
import com.sinc.thread.shared.SharedObj;
public class PrtThread implements Runnable {
private char charValue;
private SharedObj moniter;
public PrtThread() {}
public PrtThread(char charValue, SharedObj moniter) {
this.charValue = charValue;
this.moniter = moniter;
}
@Override
public void run() {
synchronized(moniter) {
/*
* 선행되어야 하는 proc 이 있는데, 실행되지 않았을 경우 wait 시킨다.
* 선행되는 proc 이 완료된 후에는, notify() 를 호출해야 한다.
* 깨워야 하는 쓰레드가 많을 경우에는, notifyAll() 을 실행해야 한다.
*/
for(int i = 0 ; i < 10 ; i++) {
this.moniter.printChar(charValue);
}
}
}
}
package com.sinc.thread.shared;
public class SharedObj {
public synchronized void printChar(char value) {
for(int i = 0 ; i < 10 ; i++) {
System.out.print(value);
}
System.out.println();
}
}
[Stream]
Stream 은 Byte 단위로 입출력을 처리하는 방식이 있고(1바이트로 입력을 받는다. 한글은 2바이트이므로 입력받을수 없다.), char 단위로 입력을 담당하는 방식이 있다.
java.io.* 안에 Stream 패키지가 있다.
- Byte 단위의 Stream : xxxStream 의 방식의 이름으로 이루어져 있다.
- Char 단위의 Stream : xxxReader/Writer 의 방식으로 이름이 이루어져 있다.
(Scanner 의 경우, 1바이트를 2바이트로 변환시켜 주는 기능이 내장되어 있다.)
- xxxReader 는 xxxStream 을 매개변수로 받을 수 있다.
- 대부분의 경우, 하나의 Stream 은 read / write 둘 중 하나만 수행하게 된다.
- Stream 과 관련된 함수들은 모두 예외를 throw 한다. 유저는 이를 예외처리 해주어야 한다.
* 통신하는 과정에서, Object 를 어떻게 다른 클라이언트에 넘길 수 있을까 ?
- Serializable 이라는 인터페이스를 구현하고 있어야 한다. byte Stream 에서 ObjectInput / ObjectOutput으로 시작하는 객체만 구현하면 된다.
- 나의 PC 에 있는 주소 번지 객체를 넘긴다고 해서 다른 PC 에서 이해할 수 있을까 ? 불가능하다. 객체를 그대로 넘겨줄 수는 없지만, 객체를 ByteStream 으로 바꾸어서 다른 클라이언트에 넘겨주게 된다.
- Serializable 인터페이스는 추상 매서드를 구현하고 있지 않은 인터페이스이다. (이러한 인터페이스를 마킹 인터페이스라고 한다.)
- Collections 는 Serialiazable 을 구현하고 있기 때문에, 객체 형식으로 데이터를 주고받는 것이 가능하다.
- String 은 Serialiazable 을 구현하고 있기 때문에, 객체 형식으로 데이터를 주고받는 것이 가능하다.
package com.sinc.stream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
// char 단위로 output 을 하기 위해 사용한다.
import java.io.FileWriter;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
public class StreamObj {
public void saveToFile() {
File file = null;
FileWriter writer = null;
BufferedWriter buffer = null;
//// Object Stream (직렬화 된 데이터를 보내기 위해선 byte 단위로 보내야 하기 때문에, reader / writer 을 활용할 수 없다.)
FileOutputStream fos = null;
ObjectOutputStream oos = null;
try {
// FileWriter
file = new File("./text.txt");
writer = new FileWriter(file);
// writer 은 2차선, buffer 은 8차선
// writer.write("2바이트 크기로 write 가능");
buffer = new BufferedWriter(writer);
buffer.write("테스트 입니다.");
// ObjectOutput
file = new File("./obj.txt");
fos = new FileOutputStream(file);
oos = new ObjectOutputStream(fos);
List<String> list = new ArrayList<>();
list.add("one"); list.add("two"); list.add("한글");
oos.writeObject(list);
System.out.println("buffer close");
} catch(Exception e) {
e.printStackTrace();
} finally {
try {
if(buffer != null) {
buffer.close();
}
} catch(Exception e) {
e.printStackTrace();
}
}
}
public void loadToFile() {
FileInputStream fis = null;
ObjectInputStream ois = null;
try {
fis = new FileInputStream("./obj.txt");
ois = new ObjectInputStream(fis);
List<String> list = (List)(ois.readObject());
System.out.println(list);
} catch(Exception e) {
e.printStackTrace();
} finally {
try {
} catch(Exception e) {
e.printStackTrace();
}
}
}
}
이것도 읽어보세요