Scanner, BufferedReader, BufferedWriter, StringBuilder 의 차이점과 사용법 주요 메서드 예시 코드 를 공부하면서 잘모르겠거나 헷갈리는 단어들을 모두 통합하여 정리해보았다.
프로그램 내에서 실행되는 가장 작은 작업 단위. Java에서는 하나의 프로그램이 여러 스레드를 가질 수 있으며, 각 스레드는 독립적으로 실행된다. 이를 통해 멀티스레딩을 구현하여 동시에 여러 작업을 수행할 수 있다.
Java에서 스레드를 생성하는 방법은 크게 두 가지가 있다.
🔸🔸 Thread 클래스 상속
class MyThread extends Thread {
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " - " + i);
}
}
public static void main(String[] args) {
MyThread thread1 = new MyThread();
MyThread thread2 = new MyThread();
thread1.start(); // 스레드 시작
thread2.start(); // 스레드 시작
}
}
🔸🔸Runnable 인터페이스 구현
이 방법이 더 선호된다.
class MyRunnable implements Runnable {
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " - " + i);
}
}
public static void main(String[] args) {
Thread thread1 = new Thread(new MyRunnable());
Thread thread2 = new Thread(new MyRunnable());
thread1.start(); // 스레드 시작
thread2.start(); // 스레드 시작
}
}
멀티스레드 환경은 여러 스레드가 동시에 실행되는 환경을 의미. Java에서 멀티스레딩을 사용하면, 프로그램이 동시에 여러 작업을 수행할 수 있어 성능을 개선할 수 있으나, 스레드 간의 자원 공유 및 동기화 문제가 발생할 수 있다.
멀티스레드 환경에서 여러 스레드가 동시에 공유 자원에 접근할 때 데이터 일관성이 깨질 수 있다. 이를 방지하기 위해 동기화(synchronization)가 필요하고, 동기화는 특정 코드 블록이나 메서드에 대해 한 번에 하나의 스레드만 접근할 수 있도록 제한한다. Java에서는 'synchronized' 키워드를 사용하여 동기화할 수 있다.
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
public class SynchronizedExample {
public static void main(String[] args) {
Counter counter = new Counter();
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
};
Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task);
thread1.start();
thread2.start();
try {
thread1.join(); // 스레드가 종료될 때까지 대기
thread2.join(); // 스레드가 종료될 때까지 대기
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Final count: " + counter.getCount()); // 예상 출력: 2000
}
}
코드 설명 : increment 메서드와 getCount 메서드는 synchronized 키워드로 동기화되어, 여러 스레드가 동시에 접근할 때 데이터 일관성을 유지할 수 있다.
개행 문자(newline character)는 줄 바꿈을 나타내는 문자. 운영체제마다 개행 문자가 다르다.
Java에서는 System.lineSeparator() 메서드를 사용하여 시스템 독립적인 개행 문자를 사용할 수 있다.
Java 7에서 도입된 try-with-resources 구문은 자원을 자동으로 닫아주는 기능을 제공한다. AutoCloseable 인터페이스를 구현한 객체는 try 블록이 끝나면 자동으로 닫힌다.
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class TryWithResourcesExample {
public static void main(String[] args) {
try (BufferedReader br = new BufferedReader(new FileReader("example.txt"))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
버퍼는 데이터를 임시로 저장하는 메모리 공간으로 버퍼링을 통해 I/O 작업의 효율성을 높일 수 있다. 버퍼 크기는 버퍼의 크기를 설정하는 것을 의미하며, 적절한 크기의 버퍼를 사용하면 I/O 성능을 최적화할 수 있다.
// 버퍼 크기 설정 예시 코드
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class BufferedReaderExample {
public static void main(String[] args) {
try (BufferedReader br = new BufferedReader(new FileReader("example.txt"), 8192)) { // 8KB 버퍼 크기 설정
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
버퍼를 사용하면 여러 번의 작은 I/O 작업을 하나의 큰 작업으로 합쳐서 처리할 수 있다. BufferedWriter에서 flush() 메서드를 호출하면 버퍼에 저장된 데이터를 강제로 파일로 밀어내는데, 이는 버퍼가 가득 차기 전에 데이터를 출력해야 할 때 유용하다.
// flush 예시 코드
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class BufferedWriterExample {
public static void main(String[] args) {
try (BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"))) {
for (int i = 0; i < 100; i++) {
bw.write("This is line number " + (i + 1));
bw.newLine();
if (i % 10 == 0) {
bw.flush(); // 10줄마다 flush() 호출
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
불변 객체(immutable object)는 생성된 이후 그 상태를 변경할 수 없는 객체. String 클래스가 대표적인 불변 객체이다. 불변 객체는 안전하게 공유될 수 있으며, 멀티스레드 환경에서 데이터 일관성을 유지하는 데 유리하다.
// 불변 객체 예시 코드
public class ImmutableExample {
public static void main(String[] args) {
String s1 = "Hello";
String s2 = s1.concat(", World!");
System.out.println(s1); // 출력: Hello
System.out.println(s2); // 출력: Hello, World!
}
}
가변 객체(mutable object)는 생성된 이후에도 그 상태를 변경할 수 있는 객체. StringBuilder와 StringBuffer가 대표적인 가변 객체이다. 문자열을 빈번하게 수정할 때 가변 객체를 사용하면 성능이 개선된다.
// 가변 객체 예시 코드
public class MutableExample {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder("Hello");
sb.append(", World!");
System.out.println(sb.toString()); // 출력: Hello, World!
}
}
파일 I/O는 파일을 읽거나 쓰는 작업을 의미한다. Java에서는 다양한 클래스를 통해 파일 I/O를 수행할 수 있다. FileReader, FileWriter, BufferedReader, BufferedWriter 등이 대표적입니다.
// 파일 읽기 와 쓰기 얘시 코드
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class FileIOExample {
public static void main(String[] args) {
// 파일 쓰기
try (BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"))) {
bw.write("Hello, world!");
bw.newLine();
} catch (IOException e) {
e.printStackTrace();
}
// 파일 읽기
try (BufferedReader br = new BufferedReader(new FileReader("output.txt"))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
