자원을 닫을 때 InterruptedException을 던지지 않는 것이 권장됩니다. 이는 스레드의 상태에 영향을 미치고, 런타임 중 오작동을 일으킬 수 있기 때문입니다.
AutoCloseable.close()는 여러 번 호출될 때 여러 번 자원을 해제하는 동작을 할 수 있다(idempotent하지 않을 수 있다). 하지만, 자원을 안전하게 관리하기 위해 idempotent하도록 구현하는 것이 권장됩니다.
try (BufferedReader reader = new BufferedReader(new FileReader("input.txt"));
BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
writer.write(line);
writer.newLine();
}
} catch (IOException e) {
e.printStackTrace();
}
요약:
1. AutoCloseable: 자원을 자동으로 해제할 수 있게 하는 인터페이스로, close() 메소드에서 자원을 해제하고 필요 시 예외를 던질 수 있음. 여러 번 호출 시에도 자원이 안전하게 해제되도록 idempotent하게 구현하는 것이 권장됨.
2. Closeable: AutoCloseable의 하위 인터페이스로, 주로 I/O 자원을 해제할 때 사용되며 close() 메소드는 IOException을 던질 수 있음.
3. try-with-resources: 자바 7에서 도입된 문법으로, 자원을 자동으로 해제할 수 있는 안전한 방법을 제공함.
Q1: AutoCloseable과 Closeable의 차이점은 무엇이며, 이 두 인터페이스를 사용하는 가장 적절한 상황은 무엇인가요?
차이점:
AutoCloseable: 자바 7에서 도입된 인터페이스로, try-with-resources 문법과 함께 사용됩니다. close() 메소드는 Exception을 던질 수 있으며, 다양한 자원 해제에 활용할 수 있습니다.
Closeable: AutoCloseable의 하위 인터페이스로, 주로 I/O 관련 클래스에서 사용됩니다. close() 메소드는 IOException을 던질 수 있으며, 특히 파일, 소켓, 스트림 같은 자원을 닫는 데 적합합니다.
사용 상황:
AutoCloseable: 모든 자원 해제를 위해 사용 가능하며, 특히 파일, 네트워크 연결, 데이터베이스 연결 등에서 자원을 안전하게 해제할 때 유용합니다. 단순한 자원이든 복잡한 자원이든, 자동으로 자원을 닫을 수 있는 장점이 있습니다.
Closeable: InputStream, OutputStream, Reader, Writer 같은 I/O 자원을 다룰 때 사용됩니다. 파일 입출력, 네트워크 소켓 등에서 사용하기 적합합니다.
정리:
자원을 수동으로 해제할 때 발생할 수 있는 문제점들은 다음과 같습니다:
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("file.txt"));
// 파일 읽기 작업
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
결론: 수동으로 자원을 해제할 때는 예외 발생 시 자원이 누수되지 않도록 많은 주의가 필요합니다. try-with-resources 문법을 사용하면 자동으로 자원을 해제해주므로 자원 누수를 방지하고 코드도 훨씬 간결해집니다.
자바에서는 AutoCloseable 외에도 자원을 관리할 수 있는 몇 가지 방법이 있습니다:
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream("file.txt");
// 파일 처리 로직
} catch (IOException e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
자원을 각 스레드마다 고유하게 관리하고 싶을 때 ThreadLocal을 사용할 수 있습니다. 이는 스레드별로 자원을 독립적으로 관리하므로, 동시성 문제를 방지할 수 있습니다.
ThreadLocal<Connection> connectionHolder = ThreadLocal.withInitial(() -> DriverManager.getConnection("jdbc:mysql://localhost/test"));
Path path = Paths.get("file.txt");
try {
List<String> lines = Files.readAllLines(path);
// 파일 내용 처리
} catch (IOException e) {
e.printStackTrace();
}
결론: 자원을 안전하게 관리하기 위한 방법은 다양합니다. AutoCloseable과 try-with-resources는 가장 직관적이고 자동화된 방법이지만, 자원 풀링이나 ThreadLocal 같은 방식을 통해 상황에 맞는 자원 관리 전략을 선택할 수 있습니다.