💡 해당 글은 『자바의 신 3판』을 복습하며 도서의 내용과 본인의 주관적인 생각을 정리한 글입니다.
☁️ 내용정리
Serializable
public interface Serializable { }
Serializable
인터페이스는 선언된 변수나 메서드가 하나도 없다.
웹 서버를 개발하다보면 생성한 객체를 파일로 저장하거나, 읽는 경우, 해당 객체를 다른 서버로 전송하거 받는 경우가 생긴다. 이럴 때 Serializable 인터페이스를 사용한다.
static final long serialVersionUID = 1L;
// String
@java.io.Serial
private static final long serialVersionUID = -6849794470754667710L;
serialVersionUID는 객체의 버전을 명시하는 데 사용된다. 따라서 Serializable 인터페이스를 구현하는 경우 해당 변수를 지정해 주는 것이 좋다. 별도로 지정하지 않는다면 컴파일러에 의해 자동으로 생성된다.
만약에 A서버에서 B서버로 DTO 객체를 전송한다고 가정해보자.
A서버와 B서버에 동일한 DTO 클래스가 존재해야 한다.
그런데 만약에 A서버의 DTO 클래스는 4개의 변수를 가지고, B서버의 클래스는 3개의 변수를 가지는 경우 제대로된 처리를 하지못하게 된다. 따라서 버전 번호로 이를 판단하기 위해 serialVersionUID 변수를 관리한다.
같은 클래스라도 serialVersionUID 값이 다르면 다른 클래스로 인식한다. 같은 serialVersionUID 값이라도 DTO 변수의 개수나 타입등이 다르면 다른 클래스로 인식한다.
package serialize;
import java.io.Serializable;
public class BookDTO implements Serializable {
@java.io.Serial
private static final long serialVersionUID = 1L;
private final String bookName;
transient private final long bookPrice;
public BookDTO(String bookName, long bookPrice) {
this.bookName = bookName;
this.bookPrice = bookPrice;
}
public String getBookName() {
return bookName;
}
public long getBookPrice() {
return bookPrice;
}
@Override
public String
toString() {
return "BookDTO{" +
"bookName='" + bookName + '\'' +
", bookPrice='" + bookPrice + '\'' +
'}';
}
}
bookDTO라는 클래스를 만들었다.
bookDTO를 파일로 저장해보자
public static void main(String[] args) {
String path = "serialize" + File.separator + "storage";
String fileName = "BookDTO.obj";
String fullPath = path + File.separator + fileName;
new File(path).mkdirs();
BookDTO bookDTO = new BookDTO("정현수", 20000L);
SerializeManager manager = new SerializeManager();
manager.saveObject(fullPath, bookDTO);
}
private void saveObject(String fullPath, BookDTO bookDTO) {
try (ObjectOutputStream outputStream =
new ObjectOutputStream(new FileOutputStream(fullPath)))
{
outputStream.writeObject(bookDTO);
} catch (IOException ignored) { }
System.out.println("Write Success");
}
이렇게 ObjectOutputStream와 FileOutputStream을 이용하면 Serializable을 구현하는 객체를 파일로 저장할 수 있다. 이제 저장한 파일을 읽어보자.
private void readObject(String fullPath) {
try (ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(fullPath))) {
BookDTO bookDTO = (BookDTO) inputStream.readObject();
System.out.println(String.valueOf(bookDTO));
} catch (Exception ignored) { }
System.out.println("Read Success");
}
BookDTO{bookName='정현수', bookPrice='0'}
Read Success
출력된 문자열을 보면 bookPrice가 0인 것을 볼 수 있다. transient
예약어가 붙은 변수는 객체를 저장하거나 전송할 때, 제외된다. 민감한 정보인 경우 해당 키워드를 통해 보호할 수 있다.
NIO
JDK 1.4
부터는 java.io
(blocking I/O)의 속도적 측면에서의 한계를 개선한 NIO
(new I/O)라는 것이 추가되었다. NIO는 non-blocking I/O
라고 흔히 불리며, stream이 아닌 buffer
와 channel
을 사용한다.
NIO에서는 쓰거나 읽을 데이터를 buffer에 저장하고 channel을 통해 전송한다.
도서에서는 나오지 않았지만 multiplexing을 Selectors
를 통해 제공한다.
☁️ 내 생각
Blocking I/O vs Non-Blocking I/O
Blocking I/O는
스레드에서 요청한 하나의 I/O에 대해 응답(자원)이 올 때까지 Blocked 상태로 대기하는 I/O처리 동기 방식을 의미한다.
Non-Blocking I/O
는 스레드에서 I/O를 요청하고 요청이 성공적으로 전달됐다는 응답을 받은 뒤, 자원을 기다리며 Blocked 상태에 들어가지 않는다. 요청을 보내놓고 본인의 로직을 수행하다가 자원을 응답받을 수 있는지 확인하고, 응답받을 수 있다면 자원을 처리하는 비동기 방식을 의미한다.
Multiplexing
Non-blocking IO(이하 넌블럭킹) 처리 방식에는 하나의 문제점이 있는데, 만약 서버의 스레드가 데이터를 읽는 작업을 요청 한 뒤, 다른 로직을 수행하고 있다고 가정한다. 앞서 넌블럭킹 방식은 데이터를 읽을 수 있는지 반복적으로 확인하는 작업을 수행한다고 했다.
이 때, 데이터를 쓰는 클라이언트 측의 스레드가 10초동안 장애를 일으켜 데이터를 쓰지 못한다면 서버의 스레드는 데이터를 읽을 수 있는지 확인하는 작업을 수없이 반복할 것이고, 이는 CPU 사이클을 낭비하게 된다.
이러한 문제를 해결하는 방식은 여러가지가 있는데, 대표적인 방식은 Mutiplexing
방식이다.
Mutiplexing이란 단일 스레드 모니터링 방식이다. 하나의 스레드가 여러 채널을 감시하면서, 이벤트가 발생한 채널에 대해 작업을 수행할 수 있다.
OS단의 개념과 웹 소켓 통신에 비유하여 나중에 자세하게 정리할 것
☁️ 질문
- serializable 이란?
- java.io는 어떤 자원을 통해 I/O를 처리하는가
- NIO는 어떤 자원을 통해 I/O를 처리하는가
- Blocking I/O와 Non-Blocking I/O의 차이
- Multiplexing 이란?
'JAVA > 자바의 신' 카테고리의 다른 글
28장. 다른 서버로 데이터를 보내려면 어떻게 해면 되나요? (0) | 2024.01.11 |
---|---|
26장. 파일에 있는 것을 읽고 쓰려면 아이오를 알아야죠 (0) | 2024.01.10 |
25장. 스레드는 개발자라면 알아두는 것이 좋아요 (1) | 2024.01.10 |
23, 24장. 자바랭 다음으로 많이 쓰는 애들은 컬렉션 - Set, Queue, Map (2) | 2024.01.10 |
22장. 자바랭 다음으로 많이 쓰는 애들은 컬렉션 - List (1) | 2024.01.10 |