[19일차]파일입출력,쓰레드,

유태형·2022년 5월 20일
0

코드스테이츠

목록 보기
19/77

오늘의 목표

  1. InputStream / OutputStream
  2. FileReader / FileWriter
  3. File
  4. 스레드(Thread)



내용

InputStream / OutputStream

자바에서는 입출력을 다루기 위한 InputStream, OutputStream을 제공합니다. 스트림은 단방향으로만 데이터를 제공할 수 있어서 입력과 출력이 각각 따로 필요합니다.

어떤 데이터를 다루느냐, 어떻게 데이터를 다룰거냐에 따라, InputStream, OutputStream을 상속한
xxxInputStream, xxxOutputStream형식으로 구현이 가능하며 기능을 추가하기 위해 생성자 호출시 매개변수로 기능을 덧댈수 있습니다.



FileInputStream

package JAVA_Effective;

import java.io.BufferedInputStream;
import java.io.FileInputStream;

public class FileInputStreamExample {
    public static void main(String[] args) {
        try{
            FileInputStream fileInput = new FileInputStream("codestates.txt");
            //파일로 부터 읽어들이는 스트림 객체 생성
            BufferedInputStream bufferedInput = new BufferedInputStream(fileInput);
			//읽어 들인 스트림을 버퍼로 저장하는 스트림 객체 생성
            int i=0;
            while((i=bufferedInput.read())!= -1){ //한 char씩 읽기
                System.out.println((char)i);
            }

            fileInput.close(); //스트림 닫기
        }catch(Exception e){
            System.out.println(e);
        }
    }
}
메서드설명
read()파일에서 1byte씩 읽어 들입니다.
close()스트림 연결을 끊습니다.


FileOutputStream

package JAVA_Effective;

import java.io.FileOutputStream;

public class FileOutputStreamExample{
    public static void main(String[] args) {
        try{
            FileOutputStream fileOutput = new FileOutputStream("codestates.txt");
            //쓰기 스트림 객체 생성
            String word = "code"; //쓸 문자열

            byte b[] = word.getBytes(); //문자열 -> 바이트배열
            fileOutput.write(b); //스트림 객체를 통해 쓰기
            fileOutput.close(); //스트림 객체 닫기
        }catch(Exception e){
            System.out.println(e);
        }
    }
}
메서드설명
getBytes()문자열을 바이트배열로 리턴합니다.
write(Byte[] b)바이트 배열을 파일에 씁니다.
close()스트림 연결을 끊습니다.



FileReader / FileWriter

InputStreamOutputStream은 입출력단위가 1Byte 16진수 입니다. 따라서 애플리케이션이 문자열로 인코딩 해주지 않는다면 사람은 알아볼수 없습니다. 2byte(char 단위)기반 문자 스트림을 제공하는것이 WriterReader입니다.

WriterReader는 여러 종류의 인코딩(encoding)과 자바에서 사용하는 유니코드(UTF-16)간의 변환을 자동으로 처리합니다.

Input, Output Stream과 마찬가지로 어떤 데이터를 다루느냐, 어떻게 데이터를 다룰거냐에 따라, Reader, Writer 상속한 xxxReader, xxxWriter형식으로 구현이 가능하며 기능을 추가하기 위해 생성자 호출시 매개변수로 기능을 덧댈수 있습니다.



FileReader

package JAVA_Effective;

import java.io.FileReader;
import java.io.IOException;

public class FileReaderExample {
    public static void main(String[] args) {
        try{
            String fileName = "codestates.txt"; //파일 경로
            FileReader file = new FileReader(fileName);
            //문자 기반 스트림 객체 생성

            int data =0;
            while((data=file.read())!=-1){ //한 글자씩
                System.out.println((char)data);
            }
            file.close();
        }catch(IOException e){
            e.printStackTrace();
        }
    }
}

FileInputStream과는 다르게 사람이 이해할 수 없는 기계어가 아닌 문자로 출력됨을 확인할 수 있습니다.

FileReader file = new FileReader(파일경로);
BufferedReader buffered = new BufferedReader(file); 

InputStream과 마찬가지로 기존의 FileReader를 매개변수로 버퍼를 제공하는 BufferedReader객체를 만들 수 있습니다.



FileWriter

package JAVA_Effective;

import java.io.FileWriter;
import java.io.IOException;

public class FileWriterExample {
    public static void main(String[] args) {
        try{
            String fileName = "codestates.txt";
            FileWriter writer = new FileWriter(fileName);
			//Writer 스트림 객체 생성
            String str = "written!"; 
            writer.write(str); //byte배열로 전환할 필요가 없음
            writer.close(); //스트림 객체 닫기
        }catch(IOException e){
            e.printStackTrace();
        }
    }
}

FileWriter 스트림 객체는 UTF-8 인코딩을 내장하고 있어서 InputStream과는 다르게 문자열을 byte배열로 전환하여 스트림 객체에 넘겨줄 필요가 없이 문자열로 파일에 저장합니다.




File

java.io 패키지는 기존의 파일이나 폴더를 제어 하는 데 사용하는 File 클래스를 제공합니다. File클래스를 이용해서 파일과 폴더에 다양한 기능을 제공합니다.

package JAVA_Effective;

import java.io.File;
import java.io.IOException;

public class FileExample {
    public static void main(String[] args) throws IOException {
        File file = new File("./","./코딩자투리.txt"); //현재폴더에 "코딩자투리"
        file.createNewFile(); //파일 생성

        System.out.println(file.getPath()); //파일의 상대 경로
        System.out.println(file.getParent()); //파일이 담긴 폴더의 상대 경로
        System.out.println(file.getCanonicalPath()); //파일의 절대경로(./, ../)는 생략
        System.out.println(file.canWrite()); //쓰기 가능 여부
    }
}

File 클래스의 메서드를 활용하여 파일과 폴더를 효율적으로 알고 조절 할 수있습니다.

File folder = new File("./"); //현재 폴더(파일이 있는)
File[] files = folder.listFiles(); //파일 목록을 File배열로 반환

files[i].getName(); //파일명 반환
files[i].renameTo(new File("파일 경로", "파일명")); //파일 이름 변경



스레드(Thread)

Thread는 영어로 번역하면 한가닥의 실입니다. 자바에서는 프로그램 내에서 실행중인 한가닥의 진행방향 이라고 생각하시면 됩니다. 병렬적인 처리가 필요한 프로그램인 경우 스레드가 여러개 필요합니다.



멀티 스레드(Multi-Thread)

동시에 여러 작업을 수행하려 할때 여러개의 cpu코어에 나누어주든 한개의 cpu코어가 아주 빠르게 작업을 번갈아 수행하든 작업의 진행은 있어야 합니다.

멀티 쓰레드는 여러 작업을 진행하기 위해 메인 쓰레드로부터 생성되어 각자 맡은 작업을 수행하는 작업 쓰레드가 1개이상인 경우를 말합니다.(Main만 존재한다면 단일 스레드)



메인 스레드(Main-Thread)

자바를 실행시키면 자바 애플리케이션이 자동으로 무조건 처음으로 실행시키는 스레드이고 프로그램 종료시 메인스레드도 종료됩니다. 작업 스레드들이 좌 우로 솟은 가지들이라 생각하면 메인스레드는 중앙의 큰 기둥이라 생각하시면 됩니다. 다른 스레드들을 생성하고 제어할 수 있습니다.



Thread 생성

Thread는 4가지 방법으로 생성할 수 있습니다. 크게 보면 Thread 추상 클래스를 상속받아 생성하는 방법 1가지, Runnable 인터페이스를 구현하는 방법 3가지가 존재합니다.

Thread 추상 클래스 상속

class Task extends Thread{
	public void run(){
    	//스레드가 실행할 일
    }
}

Thread 추상 클레스엔 public void run()메서드가 추상메서드로 존재합니다. 스레드를 사용하기 위해선 해당 메서드를 오버라이딩 하여 스레드가 알아서 할 일을 정의하여야 합니다.

Runnable 인터페이스 구현

자바는 다중상속이 불가능합니다. 만약 어떤 상위 클래스를 상속 받아 하위 클래스를 쓰레드로 만들고 싶다면, Thread 추상 클래스를 상속받아야 하기 때문에 구현이 불가능 할수도 있습니다.
또 간편히 쓰레드를 구현하고 싶을 때 유용합니다.

Thread 상속과 마찬가지로 Runnable 인터페이스 구현도 public void run()메서드를 오버라이딩하여 구현해야 사용 가능합니다.

  1. 일반적인 인터페이스 구현
class Task implements Runnable{
	public void run(){
    	//스레드가 실행할 일
    }
}

Runnable task = new Task();
Thread thread = new Thread(task); //쓰레드에 Runnable 삽입
  1. 익명 클래스
Thread thread = new Thread(new Runnable(){
	public void run(){
    	//스레드가 실행할 일
    }
});
  1. 람다식
Thread thread = new Thread(()->{
	//스레드가 실행할 코드
});

Runnable은 추상메서드가 public void run() 한개인 함수형 인터페이스입니다. 따라서 람다식으로 익명클래스 구현이 가능합니다.

Runnable 인터페이스를 구현한 객체는 Thread를 상속한 객체와 다릅니다. public void run()메서드를 오버라이딩해야 하는 것은 유사하지만, Runnable 인터페이스를 구현한 객체는 Thread를 상속 받은 객체가 아니므로 Thread객체를 따로 만들어 Runnable 인터페이스를 구현한 객체를 인자로 전달해야합니다.

스레드 실행은 오직 Thread와 Thread를 상속받은 하위 객체만 가능합니다.

스레드.start(); // 해당 스레드를 실행합니다.

스레드 객체를 만들었다고 바로 스레드를 실행할수 있는 것은 아닙니다. 반드시 start()메서드를 호출하여야만 비로소 실행됩니다.

스레드 이름

Thread.currentThread().getName(); //현지 스레드의 이름을 반환 받습니다.
//currnetThread()현재 실행중인 스레드 객체를 반환합니다.



스레드의 상태

Thread는 단순히 실행만 되는것이 아니라 실제로 CPU나 다른 하드웨어 자원을 필요로 하므로 여러가지의 상태를 가지고 각 상태마다 다른 행동을 취합니다. .start()한다고 하여 바로 실행 되는 것이 아닌 실행준비상태가 되고 실제로 실행 되는것은 JVM에 의해 CPU를 할당받아야 실행 할 수 있습니다.

상태설명
NEW스레드 객체 생성시 상태(start()함수 호출x)
RUNNABLEstart()메서드 호출시 실행 준비 단계
WAITINGwait(),join(),sleep()등 외부의 통제에 의해 대기 상태
TIMED_WAITING할당된 CPU시간을 모두 소모해 다음 할당을 기다리는 상태
BLOCKED대기 이지만 CPU할당은 못 받고, RUNNABLE상태로 바꿔줘야 받을 수 있는 상태
TERMINATED모든 작업을 완료후 종료된 상태

메서드설명
interrupt일시 정지 상태의 스레드에서 InterruptedException 예외를 발생시켜, 예외 처리 코드(catch)에서 실행 대기 상태로 가거나 종료 상태로 갈 수 있도록 합니다.
notify(),notifyAll()wait() 메서드에 의해 일시 정지 상태에 있는 스레드를 실행 대기 상태로 만듭니다
sleep(long millis), sleep(long millis,int nanos)주어진 시간 동안 스레드를 일시 정지 상태로 만듭니다. 주어진 시간이 지나면 자동적으로 실행 대기 상태가 됩니다.
join(), join(long millis), join(long millis,int nanos)join() 메서드를 호출한 스레드는 일시 정지 상태가 됩니다. 실행 대기 상태로 가려면 join() 메서드를 멤버로 가지는 스레드가 종료되거나, 매개값으로 주어진 시간이 지나야 합니다.
wait(),wiat(long millis)동기화(synchronized) 블록 내에서 스레드를 일시 정지 상태로 만듭니다. 매개값으로 주어진 시간이 지나면 자동적으로 실행 대기 상태가 됩니다. 시간이 주어지지 않으면 notify(), notifyAll() 메서드에 의해 실행 대기 상태로 갈 수 있습니다.
yield실행 중에 우선순위가 동일한 다른 스레드에게 실행을 양보하고 실행 대기 상태가 됩니다.

wait() 메서드와 notify(), notifyAll()메서드는 모두 하나의 객체를 여러 쓰레드가 공유하는 방식입니다. 따라서 하나의 객체는 한번에 하나의 쓰레드만 접근할 수 있어야 하며 그 구간을 정해주는 Synchronized 블록 또는 메서드 처리된 구간에서만 여러 쓰레드가 한개씩 접근 가능합니다.



쓰레드 풀

여러개의 쓰레드를 생성하고 관리하고 제한하는 풀이 존재 한다면, 시스템이 쓰레드로 인한 성능저하가 되거나 과도한 쓰레드로 시스템이 다운되는 불상사를 방지할 수 있을 것입니다. 자바에서는 ExcutorService 인터페이스를 상속한 Excutors클래스의 newCachedThreadPool() 또는 newFixedThreadPool()메서드로 쓰레드 풀 객체를 생성할 수 있습니다.

쓰레드 풀 메서드

메서드리턴타입설명
shutdown()void작업 큐에 남아 있는 모든 작업을 처리한 뒤 종료
shutdownNow()List작업 큐에 남아 있는 작업들을 즉시 종료후, 남은 작업들을 리스트로 반환
awaitTermination(Long timeout,TimeUnit unit)boolean정해진 시간안에 남은 스레드들을 처리하면 ture, 다 하지 못하면 남은 작업은 interrupt()하고 false반환
package JAVA_Effective;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

public class ThreadPoolExample {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(6);
		//Executorservice 라이브러리 객체에서 쓰레드풀을 가져옵니다.
        for(int i=0;i<10;i++){
            Runnable runnable = new Runnable(){
               public void run(){
                    ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executorService; //쓰레드 풀 주소를 가져옵니다.(형변환)
                    int poolSize = threadPoolExecutor.getPoolSize(); //쓰레드 수
                    String threadName = Thread.currentThread().getName(); //쓰레드 이름
                   System.out.println("스레드풀 갯수:" + poolSize + "스레드 이름: "+threadName);
                }
            };

            executorService.execute(runnable); //쓰레드 풀에 쓰레드 삽입

            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        executorService.shutdown(); //쓰레드 풀 종료
    }
}
//Console
스레드풀 갯수:1스레드 이름: pool-1-thread-1
스레드풀 갯수:2스레드 이름: pool-1-thread-2
스레드풀 갯수:3스레드 이름: pool-1-thread-3
스레드풀 갯수:4스레드 이름: pool-1-thread-4
스레드풀 갯수:5스레드 이름: pool-1-thread-5
스레드풀 갯수:6스레드 이름: pool-1-thread-6
스레드풀 갯수:6스레드 이름: pool-1-thread-1
스레드풀 갯수:6스레드 이름: pool-1-thread-2
스레드풀 갯수:6스레드 이름: pool-1-thread-3
스레드풀 갯수:6스레드 이름: pool-1-thread-4



후기

어제는 람다식, 스트림등을 하였고 오늘은 IO와 스레드에 대하여 공부하였습니다. 비교적 최근에 나온 기술들이고 사실 웹 개발엔 엄청 중요한것은 아니지만 지금 체화시켜 놓으면 훨씬 원할히 일할 수 있을 것 같습니다.




GitHub

https://github.com/ds02168/CodeStates/tree/main/src/JAVA_Effective

profile
오늘도 내일도 화이팅!

0개의 댓글