스트림은 단방향으로만 전송할 수 있기에 입력 출력 동시에 처리하기위해서는 각각의 스트림이 필요하다
FileInputStream
FileOutputStream
FileReader / FileWriter
인코딩을 유니코드로 / 유니코드를 인코딩으로 변환
스레드
프로세스는 실행 중인 애플리케이션
실행에 필요한 만큼 메모리를 할당받아 프로세스가 된다
프로세스는 데이터,컴퓨터자원,스레드로 구성
스레드는 데이터와 애플리케이션이 확보한 자원을 활용하여 소스코드를 실행 = 하나의 코드 실행흐름
메인스레드(Main thread)
메인스레드가 main 메서드를 실행
멀티 스레드(Multi-Thread)
여러 스레드가 동시에 작압을 수행
멀티태스킹을 구현하는데 핵심적인 역할
작업 스레드 생성과 실행
run()
1. Runnable 인터페이스를 구현한 객체에서 run()을 구현하서 스레드를 생성하고 실행
@는 main 메서드의 반복문에서 출력한 문자 = 메인 스레드의 반복문 코드
#은 run() 메서드의 반복문에서 출력한 문자 = 작업 스레드의 반복문 코드
@ 와 #이 섞여 있다 = 메인스레드와 작업스레드가 동시에 병렬로 실행
2. Thread 클래스를 상속받은 하위 클래스에서 run()을 구현하여 스레드를 생성하고 실행
익명객체를 사용하여 스레드 생성하고 실행하기
스레드가 수행할 동작은 run()메서드의 바디에 작성해야하며 자바는 객체지향 언어라서 클래스 안에 코드를 작성해야한다
하지만 클래스를 따로 정의하지 않고 익명객체를 활용해서 스레드를 생성하고 실행할 수 있다
자바(Java)에서 익명 객체인지를 판단하는 가장 간단한 방법은 객체가 이름이 없고, 클래스를 정의하고 객체를 생성하는 한 줄로 표현된 경우
- 이름 없음: 객체에는 이름이 없습니다. 즉, 변수에 할당되지 않고 클래스 이름이 없습니다.
- 객체 생성과 동시에 정의: 객체를 생성하는 동시에 클래스를 정의합니다. 클래스의 이름이 없으므로 익명 객체가 만들어집니다.
Runnable 익명 구현 객체를 활용한 스레드 생성 및 실행
public class ThreadExample1 {
public static void main(String[] args) {
// 익명 Runnable 구현 객체를 활용하여 스레드 생성
Thread thread1 = new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 100; i++) {
System.out.print("#");
}
}
});
thread1.start();
for (int i = 0; i < 100; i++) {
System.out.print("@");
}
}
}
Thread 익명 하위객체를 활용한 스레드 생성 및 실행
public class ThreadExample2 {
public static void main(String[] args) {
// 익명 Thread 하위 객체를 활용한 스레드 생성
Thread thread2 = new Thread() {
public void run() {
for (int i = 0; i < 100; i++) {
System.out.print("#");
}
}
};
thread2.start();
for (int i = 0; i < 100; i++) {
System.out.print("@");
}
}
}
스레드의 이름
스레드의 이름 조회하기
스레드의_참조값.getName()
스레드의 이름 설정하기
스레드의_참조값.setName()
스레드 인스턴스의 주소값 얻기
currentThread()
스레드의 동기화
Thread.sleep(1000);
스레드를 일시정지 시키는 메서드
스레드가 일시정지되면 대기열에서 기다리고있던 다른 스레드가 실행된다
반드시 try .. catch 문의 try 블록 내에 작성해주어야 한다
임계영역(Critical section)과 락(Lock)
임계영역은 오로지 하나의 스레드만 코드를 실행할 수 있는 코드영역
락은 임계영역을 포함하고 있는 객체에 접근할 수 있는 권한을 의미
임계영역으로 설정된 객체가 다른 스레드에 의해 작업이 이루어지고 있지않을 때
임의의 스레드 A는 해당 객체에 대한 락을 획득하여 임계 영역 내의 코드를 실행 할 수 있다
스레드 A가 임계영역 내의 코드를 실행 중일때에는
다른 스레드들은 락이 없으므로 이 객체의 임계영역 내의 코드를 실행할 수 없다
스레드 A가 코드를 모두 실행하면 락을 반납
이때부터 다른 스레드중 하나가 락을 획득하여 임계영역 내의 코드를 실행 가능
1. 메서드 전체를 임계 영역으로 지정하기
synchronized 메서드 전체를 임계영역으로 설정
메서드가 호출되었을 때 메서드를 실행 할 스레드는 메서드가 포함된 객체의 락을 얻는다
class Account {
...
//withdraw()가 호출되면 실행되는 스레드는
//withdraw()가 포함된 객체의 락을 얻으며
//해당 스레드가 락을 반납하기 이전에 다른 스레드는
//해당 메서드의 코드를 실행하지 못하게 된다
public synchronized boolean withdraw(int money) {
if (balance >= money) {
try { Thread.sleep(1000); } catch (Exception error) {}
balance -= money;
return true;
}
return false;
}
}
2. 특정한 영역을 임계 영역으로 지정하기
특정영역을 임계영역으로 지정하려면 synchronized 키워드와 함께 소괄호() 안에
해당영역이 포함된 객체의 참조를 넣고 {} 중괄호 블록을 열어 블록내에 코드 작성
class Account {
...
public boolean withdraw(int money) {
synchronized (this) {
if (balance >= money) {
try { Thread.sleep(1000); } catch (Exception error) {}
balance -= money;
return true;
}
return false;
}
}
}
JVM
자바로 작성한 소스코드를 해석해 실행하는 별도의 프로그램
자바프로그램과 운영체제 사이에서 통역가 역할을 수행한다
Stack 과 Heap
JVM메모리 구조
Stack영역
스택은 일종의 자료구조
자료구조는 프로그램이 데이터를 저장하는 방식
저장하는 방식중 하나가 스택이다
LIFO 맨 마지막에 들어온 테이터가 가장 먼저 나가는 자료구조
Heap영역
JVM이 작동되면 자동생성
객체나 인스턴스변수 배열이 저장된다
Person person = new Person();
new Person()이 실행되면 Heap영역에 인스턴스가 생성되며
인스턴스가 생성된 위치의 주소값을 person 에게 할당해주느데 이 person은 Stack영역에 선언된 변수
객체를 다룬다는 것은 Stack영역에 저장되어있는 참조변수를 통해 Heap영역에 존재하는 객체를 다룬다
Heap영역은 실제 객체의 값이 저장되는 공간
Garbage Collection
가비지컬렉션 메모리를 자동으로 관리하는 프로세스
프로그램에서 더이상 사용하지 않는 객체를 찾아 삭제하거나 제거하여 메모리를 확보하는 것
동작방식
Heap영역은 객체는 대부분 일회성 메모리에 남아있는 기간이 대부분 짧다는 전제로 설계
Young / Old 두가지로 나뉜다
Young 새롭게 생성된 객체가 할당 많은 객체가 생성되었다 사라지는것을 반복
Minor GC
Old Young 영역에서 상태를 유지하고 살아남은 객체들이 복사되는 곳
보통 Young영역보다 크게 할당되고 큰만큼 가비지는 적게 발생
Major GC
가비지컬렉션이 실행될때 두가지 단계를 따른다
1. Stop The World
JVM이 애플리케이션의 실행을 멈추고 가비지컬렉션이 실행
2. Mark and Sweep
1번을 통해 모든 작업이 중단되면 모든 변수와 객체를 탐색해서 사용되지않는 메모리 제거
'개발공부🌷 > JAVA' 카테고리의 다른 글
JAVA 심화 애너테이션 , 람다 , 스트림 (0) | 2023.11.02 |
---|---|
JAVA 예외처리 (0) | 2023.11.01 |
JAVA 컬렉션 (0) | 2023.10.30 |
JAVA 기초11-1 객체지향프로그래밍 심화 (0) | 2023.10.26 |
JAVA 기초 11 객체지향프로그래밍 심화 (0) | 2023.10.25 |