접근 제어자(Access modifier)

  • Public
    • (패키지에 상관없이) 제약없이 사용이 가능하다.
  • protected
    • 부모 클래스에서는 public
    • 외부에서는 private
    • 다른 패키지인 경우, 상속 관계 있으면 사용 가능
  • pacakge(default)
    • 키워드를 사용하지 않았을 경우 default로 사용
    • 같은 패키지 이내에서만 사용 가능
  • private
    • 같은 클래스 안에서만 사용 가능
package com.kakao.test;

public class Student {
	// 결론적으로 field는 특별한 이유가 없는 한 싹다 private
	// class 내부의 필드는 보호해야하는 정
	private String stuName; // information hiding
	private String strNum;
	
	public String getStuName() {
		return this.stuName;
	}
	
	/**
	 * @param stuName
	 */
	public void setStuName(String stuName) {
		this.stuName = stuName;
	}
	
	
	public String getStrNum() {
		return strNum;
	}

	public void setStrNum(String strNum) {
		this.strNum = strNum;
	}

	public Student(){
		
	}
	// method는 행위를 하는 작업이기 때문에 특별한 이유가 없는 한 
	// 외부에서 사용할 수 있도록 public으로 설정한다. 
}

 


래퍼 클래스(Wrapper Class)

  • Primitive(기본 타입) type의 데이터를 객체로 취급해야 하는 경우가 생길 때 사용
  • 기본 타입 데이터를 객체로 포장해주는 클래스
  • 래퍼 클래스는 인스턴스에 저장된 값을 변경할 수 없기 때문에, 새로운 인스턴스 생성 후 생성된 인스턴스 값만 참조 가능
  • 박싱(Boxing) : 기본 타입 데이터를 래퍼 클래스의 인스턴스로 변환하는 과정
  • 언박싱(UnBoxing) : 래퍼 클래스의 인스턴스에 저장된 값을 다시 기본 타입의 데이터로 꺼내는 과정
  • JDK 1.5부터 컴파일러가 자동으로 박싱, 언박싱 처리 -> 오토 박싱, 오토 언박싱

 


제너릭(generic)

  • 데이터의 타입(data type)을 일반화(generalize) 하는 것
  • 클래스나 메소드에서 사용할 내부 데이터 타입을 컴파일 시에 미리 지정하는 방법
    • 객체의 타입 안정성 증가
    • 반환 값에 대한 타입 검사 비용 절감
  • JDK 1.5 이전까지는 Object를 사용했기 때문에, 원하는 타입으로 변환할 때 오류 발생 가능성이 있었다.
  • 제네릭 도입 이후 타입 변환과 같은 번거로운 작업 생략 가능
  • 타입 변수(type variable) : 임의의 참조형 타입을 의미한다.
    • 제네릭 클래스를 생성할 때, 타입 변수 자리에 사용할 실제 타입 명시 필요
    • 타입 변수 자리에는 기본 클래스가 아닌, 래퍼 클래스를 사용
    • Java SE 7부터는, 타입 추정이 가능한 경우, 타입 생략 가능
MyArray<Integer> myArr = new MyArray<Integer>();
MyArray<Integer> myArr = new MyArray<>();
import java.util.*;

class LandAnimal { public void crying() { System.out.println("육지동물"); } }
class Cat extends LandAnimal { public void crying() { System.out.println("냐옹냐옹"); } }
class Dog extends LandAnimal { public void crying() { System.out.println("멍멍"); } }
class Sparrow { public void crying() { System.out.println("짹짹"); } } 

class AnimalList<T> {
	ArrayList<T> al = new ArrayList<T>();
	
	void add(T animal) { al.add(animal); }
	T get(int index) { return al.get(index); }
	boolean remove(T animal) { return al.remove(animal); }
	int size() { return al.size(); }
}

public class prog {
	public static void main(String[] args) {
		AnimalList<LandAnimal> landAnimal = new AnimalList<>();	// Java SE 7부터 생략가능함.
		
		landAnimal.add(new LandAnimal());
		landAnimal.add(new Cat());
		landAnimal.add(new Dog());
		// landAnimal.add(new Sparrow());	// 오류가 발생함.
		
		for (int i = 0; i < landAnimal.size() ; i++) {
			landAnimal.get(i).crying();
		}
	}
}

 


컬렉션 프레임워크(Collection Framework)

  • 다수의 데이터를 쉽고 효과적으로 처리할 수 있는 표준화된 방법을 제공하는 클래스의 집합
  • 데이터를 저장하는 자료구조와 데이터를 처리하는 알고리즘을 구조화하여 클래스로 구현해 놓은 것
  • 인터페이스를 사용하여 구현
    • List, Set, Map

 

 

스트림(Stream)

  • 파일이나 콘솔의 입출력을 직접 다루지 않고, 스트림이라는 흐름 사용
  • 실제 입력이나 출력이 표현된 데이터의 이상화된 흐름
  • java.io 
    • InputStream
    • OutputStream
  • 보조 스트림 : 실제 데이터를 주고받지는 않지만, 다른 스트림의 기능을 향상시키거나 새로운 기능을 추가
    • 여러 개 Stream을 결합해서 확장 가능 

 

람다 표현식(lambda expression)

  • 메소드를 하나의 식으로 표현한 것
  • 객체를 생성하지 않아도, 메소드 사용 가능
  • Java SE 8 부터 람다 표현식이 생겼고, 함수형 프로그래밍 가능
  • 유의사항
    • 매개변수 타입의 추론이 가능한 경우 생략 가능
    • 매개변수가 하나일 때 괄호 생략 가능
    • 함수의 몸체가 하나의 명령문일 때 중괄호 생략 가능
    • 함수의 몸체가 하나의 return 문일 때 중괄호 생략 불가능 
    • return문 대신에 표현식 사용 가능
int min(int x, int y) {
    return x < y ? x : y;
}

// lambda expression
// (매개변수 목록) -> {함수 몸체}
(x, y) -> x < y ? x : y;
new Thread(new Runnable() {
    public void run() {
        System.out.println("전통적인 방식의 일회용 스레드 생성");
    }
}).start();
 
new Thread(()->{
    System.out.println("람다 표현식을 사용한 일회용 스레드 생성");
}).start();

 

 

함수형 인터페이스(functional interface)

  • 람다 표현식을 하나의 변수에 대입할 때 사용하는 참조 변수의 타입
  • 추상 클래스와 다르게, 하나의 추상 메소드만 가진다.
  • 어노테이션을 사용해 함수형 인터페이스 명시
@FunctionalInterface
interface Calc { // 함수형 인터페이스의 선언
    public int min(int x, int y);
}
 
public class Lambda02 {
public static void main(String[] args){
        Calc minNum = (x, y) -> x < y ? x : y; // 추상 메소드의 구현
        System.out.println(minNum.min(3, 4));  // 함수형 인터페이스의 사용
    }
}

 

스트림 API

  • Java SE 8부터 생긴 개념
  • 배열이나 컬렉션에 접근하기 위해서는 반복문이나 반복자(iterator)를 사용해야 한다. -> 가독성 저하, 코드 재사용 불가능
  • 정형화된 데이터의 단점을 스트림 API은 데이터를 추상화해서 다뤄 다양한 방식으로 저장된 데이터를 읽고 쓰기 가능하다.
  • 스트림 API 특징
    • 외부 반복을 통해 작업하는 컬렉션과 다르게 내부 반복을 통해 작업 수행
    • 재사용이 가능한 컬렉션과 다르게 한 번 사용 가능
    • 원본 데이터 변경 X
    • 연산은 필터-맵(filter-map) 기반의 API를 사용해서 성능 최적화
    • parallelStream() 메소드를 통해 손쉬운 병렬 처리 지원
  • 동작 흐름
    • 1. 스트림의 생성
    • 2. 스트림의 중개 연산 (스트림의 변환)
    • 3. 스트림의 최종 연산 (스트림의 사용)

 

Optional Class

  • primitive 타입의 객체를 포장해주는 래퍼 클래스
  • Optional 인스턴스는 모든 타입의 참조 변수를 저장할 수 있다.
  • 예상치 못한 NullPointerException 예외를 제공되는 메소드로 간단히 회피 가능
  • null 값 예외 처리 가능

 

Optional 객체 생성

  • of() 메소드 : null 이 아닌 명시된 값을 가지는 Optional 객체 반환
  • ofNullable() 메소드 사용 
    • 명시된 값이 null이 아니면 명시된 값을 가지는 Optional 객체를 반환하고, 명시된 값이 null이면 비어있는 Optional 객체 반환 
    • null 가능성이 있을 때 사용

JDBC(Java Database Connectivity)

- Java class와 Interface의 집합

- Java 프로그램이 DB에 의존하지 않도록, 공통된 Interface를 제공하는 JDBC 이용


 

JDBC 연결

(1) JDBC driver loading

- 사용하려는 데이터베이스에 맞는 JDBC driver class 등록

- Class라는 class가 존재

- driver Manager class 이용

// 1. JDBC driver Loading
// MySQL 8.0 부터는 아래의 Class 이용
Class.forName("com.mysql.cj.jdbc.Driver");
System.out.println("드라이버 로딩 성공");

 

(2) DBMS와 연결

- 실제로 데이터베이스와 연결된다.

- 성공하면 Connection instance가 생성된다 

- 세션을 통해 생성되는 과정 때문에 부하가 많이 걸린다.

- 자원을 다 사용한 후, 해제 해주어야 한다.(close)

- Database의 Session이 닫힐 수 있도록 쿼리ㅓ 작성

- JDBC URL 

String jdbcURL = "jdbc:mysql://localhost:3306/sqldb?characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false";
con = DriverManager.getConnection(jdbcURL, "user", "password");
System.out.println("데이터 베이스 연결 성공!");

 

(3) Statement 생성

1. 일반 statement

2. preparedStatement : 1번의 개량형

3. callable statement 

- 3번은 stored procedure를 호출할 때 사용한다.

- 1, 2번은 구문으로 실행한다.

String sql = "SELECT userId, name, addr FROM usertbl";
pstmt = con.prepareStatement(sql);

 

 

(4) Query 실행

- statement를 이용해서 SQL query를 DBMS에 전달하고 실행한다.

1. execute() 

2. executeQuery() -> SELECT

3. executeUpdate() -> INSERT, UPDATE, DELETE

- 1번 X, 2, 3번 사용

rs = pstmt.executeQuery();

 

 

(5) 결과쿼리(ResultSet)

- 쿼리문을 실행한 결과 record를 가리키는 포인터

- rs.next()를 통해서 다음 행으로 넘어갈 수 있다. -> 가능하면 true, 불가능하면 false return

- rs는 쿼리를 실행시켜서 현재 데이터베이스에 있는 데이터를 그대로 가져와서 자바 프로그램에 전달해준다. 실시간으로 바뀐 데이터는 전달해주지 않는다.

- scrollable cursor은 연동이 되기 때문에 데이터베이스에 직접 접근해야해서 부하가 많이 걸린다.

ResultSet rs = pstmt.executeQuery();

- rs.getString() : String으로 타입 변환(DB의 데이터타입을 Java의 데이터타입으로 변환)

rs.getString(”name”);  // 홍길동을 java string 형태로 가져온다.
rs.getString(2); // 홍길동
// 5. 결과처리
while(rs.next()){ // rs 쭉 실행하기 -> 위에서부터 밑에까지 쭉 내려가세요
    String id = rs.getString(1);
    String name = rs.getString(2);
    String addr = rs.getString(3);
    System.out.println(id+" "+name+" "+addr);
}

Connection pool

- 동시에 많은 사용자에 대한 데이터베이스 쿼리를 제공하기 위해 사용한다.

- connection을 많은 사용자와 공유하게 되면? -> 트랜젝션 발생

- pooling 기법을 사용해서 해결한다.

 

 

pooling

- 속도 향상(생성과 소멸 과정X -> 반납 대여 과정)

- 자원의 효율성

- connection 수를 제어한다.

- 직접 구현하는 것이 아니라, Apache Commons의 DBCP 사용

 

 

Apache Commons 

- 재사용 가능한 자바 기반의 컴포넌트를 모아놓은 통합 프로젝트

- dbcp : Database connection pooling services

 

Thread 

- 생성

- start()

- sleep()

- stop() 사용 금지 -> 직접 구현 권장 : stop() 스레드 종료 , suspend() 스레드 일시 정지, resume() 일시중지 스레드 재시작

- interrupt() : interrupted(), isInterrupted()

- yield() : 스레드가 자신에게 주어진 실행시간을 모두 쓰지 않고, 다른 스레드에게 양보한다. -> 응답성을 높이기 위해 사용

 


sleep() Method 

- sleep(long millisecond) : 일정 시간동안 Thread를 중지 시킨다.

- 지정된 시간 후 자동으로 실행 대기 상태가 된다.

- 일시 정지 상태에서 Interrupt() 호출 시, InterruptException이 발생하면서 깨어난다.

- static method

 

 

join() Method

- 스레드는 각자 독립적으로 실행되지만, 다른 스레드가 종료된 이후 실행해야하는 경우가 있다.

- 실행 대기 상태로 가기 위해서는 join() 메소드를 멤버로 가지는 스레드가 종료되거나, 매가 값으로 주어진 시간이 지나야 한다.

- join()에 인자를 주지 않는 경우, 호출하는 스레드가 무한히 기다리는 상황이 생길 수 있다. -> ex. thread.join(100);

- insatnce method

 

상태 전이도 - sleep()

 

상태 전이도 - yield()

- 우선순위가 동일한 다른 스레드에게 실행을 양보해주고 실행 대기 상태로 이동

- 프로그램의 응답성을 높여준다.

 

상태 전이도 - join()


interrupt() Method

- stop() : 스레드 강제 중지, 편하지만 deprecated(사용금지)

- strat() : 스레드 시작

 

 

Thread 실행 후, 작업이 끝나기 전에 해당 Thread를 중지 시키고 싶은 경우

- interrupt() 이용

- 직접 중지 시키지 않고, 스레드의 내부 상태(Interrupted state)를 변경 시켜준다.

 

 

Thread Interrupt Method

1. interrupted() // static : 현재 실행중인 스레드가 Interrupt가 걸렸는지 알려준다.

- 상태 값 조사 후, false로 값 변경

- 2번과 다르게 값 변경이 이뤄지기 때문에 주의해서 사용해준다.

 

2. isInterrupted() // non static : 특정 스레드가 Interrupt가 잡혔는지 알려준다. 

- 현재 상태 값만 return true or false

 

 

스레드를 만드는 방법 2가지

1. Thread Class로 직접 생성, runnable interface 사용

- Runnable : 작업 스레드가 실행할 수 있는 코드를 가지고 있는 객체

- Runnable은 Interface Type -> 구현 객체 필요

- Runnable 내부에 있는 run() 메소드를 구현 클래스에서 run()을 Override를 통해 작업 스레드가 실행할 코드를 작성해야 한다.

- Runnable r = new 구현 객체 생성 후, new Thread(r) 구현 객체를 Thread 생성자의 매개 값으로 넣어주면 작업 스레드가 생성

- 생성된 스레드는 바로 실행되지 않고, thread.start()와 같이 start 메소드를 호출해야 실행된다.

- instance를 만들어서 객체를 Thread Class의 인자로 넣어준다.

 

 

2. Thread 하위 클래스로부터 생성

- Thread 하위 클래스로 작업 스레드를 정의하면서 작업 내용 포함

- Thread Class 상속 후, run()을 Override를 통해 작업 스레드가 실행할 코드를 작성한다.

- 생성된 객체에서 start() 메소드를 호출 -> run() 메소드 실행

- IS-A Relationship

- Java(단일 상속 지원)는 다중 상속 X -> Thread Class를 extends할 경우 제약이 생길 수 있다.

- Tightly Coupled 문제가 생길 수 있다.

- Main class가 실행 되면서, 메인 스레드가 생성

- Thread 객체를 생성하고, start() 메소드로 호출하면, 내부적으로 Thread stack을 할당받고, run() 호출


Thread의 이름 

- 메인 스레드 : main

- 직접 생성한 스레드 : Thread-n // n은 스레드의 번호

- setName(), getName()

- Thread.currentThread() : 객체의 참조가 없는 경우 사용

 

 

Thread 상태 전이도

 

스레드풀(ThreadPool)

- 스레드 개수가 늘어나게 되면, 생성과 스케줄링으로 CPU와 메모리 사용량이 늘어난다. -> 성능 저하

- 병렬 작업 폭증 -> 스레드 폭증 -> 성능 저하

- 스레드 풀을 사용해서 해결한다.

- 스레드 풀 : 스레드의 개수를 제한하고, 작업 큐(Queue)에 들어오는 작업들을 하나씩 맡아서 처리한다.

- ExecutorService Interface, Executors Class 사용

Thread 동기화

- 멀티 스레드에서, 여러 스레드가 하나의 객체를 공유해서 써야 하는 경우 사용

- 공유 자원의 문제가 발생 -> ex 예약 시스템

- 스레드가 사용 중인 객체를 다른 스레드가 변경할 수 없도록, 객체에 잠금을 걸어주어야 한다.

- 임계 영역(critical section)을 설정해서 문제 해결 -> lock 개념 도입

- 임게 영역 : 단 하나의 스레드만 실행할 수 있는 코드 영역

 

스레드 동기화 방법

- 임계 영역(critical section) : 공유 자원에 단 하나의 스레드만 접근하도록(하나의 프로세스에 속한 스레드만 가능)

- 뮤텍스(mutext) : 공유 자원에 단 하나의 스레드만 접근 가능하도록(서로 다른 프로세스에 속한 스레드도 가능)

- 이벤트(event) : 특정한 사건 발생을 다른 스레드에게 알린다.

- 세마포어(semaphore) : 한정된 개수를 가진 자원을 여러 스레드가 사용하려고 할 때 접근 제한

- 대기 기능 타이머(waitable timer) : 특정 시간이 되면 대기 중이던 스레드를 꺠운다.

 

 

 

동기화를 하지 않은 Code

- 스레드 2개가 동시에 실행되기 때문에 잔액이 마이너스가 나온다.

 

동기화 사용 synchronzied keywowrd

- 메소드 자체에 동기화를 하거나, 임의 영역을 설정해서 block으로 닫아주는 방법이 있다.

- 임의 영역을 설정해서 동기화 해주는 방식이 좀 더 사용하기 좋다.

- 공유 객체가 자기 자신인 경우 -> this

 

상태 전이도

- wait() : lock을 걸고 대기

 

notify() Method

- 스레드가 작업을 완료하면 notifiy를 호출해서 일시 정지 상태의 다른 스레드들을 실행 대기 상태로 옮긴다.

- 작업 완료된 스레드(자기 자신)는 wait() 메소드로 일시 정지 상태로 만든다.

- wait에서 대기하고 있는 스레드가 실행할 수 있는 상황이 되어서 block 해제

 

 

Java 고유 락(Intrinsic LocK)

  • 자바의 모든 객체는 lock을 갖고 있다.
  • synchronzied 블록은 Intrinsic lock(고유락)을 사용해서 스레드 접근을 제어한다.
  • synchronzied()
    • 1. 인자 값에 this 이용 : 여러 스레드가 들어와 서로 다른 block 호출해도 this를 사용해서 자기 자신에 lock을 걸어 기다려야 한다.
    • 2. 인자 값에 Object 이용 : block 마다 다른 Lock을 걸리게 해 효율적인 코드 작성 가능
  • structured lock(구조적 lock) : 고유 lock을 통한 동기화
  • reentarant lock(명시적 lock) : A획득 -> B획득 -> A해제 -> B해제를 가능하게 하기 위해서 사용
public class Counter{
    private Object lock = new Object(); // 모든 객체가 가능 (Lock이 있음)
    private int count;
    
    public int increase() {
        // 단계 (1)
        synchronized(lock){	// lock을 이용하여, count 변수에의 접근을 막음
            return ++count;
        }
        
        /* 
        단계 (2)
        synchronized(this) { // this도 객체이므로 lock으로 사용 가능
        	return ++count;
        }
        */
    }
    /*
    단계 (3)
    public synchronized int increase() {
    	return ++count;
    }
    */
}

Process(프로세스)
- 현재 실행중인 프로그램

- 프로세스는 최소한 1개 이상의 Thread(스레드)로 구성된다.

- 애플리케이션을 실행할 때, 운영체제로부터 실행에 필요한 메모리를 할당받아 애플리케이션의 코드를 실행하는 것

- 프로그램을 실행시키기 위해서는 OS로부터 자원(resource)를 할당 받아야 한다.

- 리소스(Code, Data, Stack, Heap) + Thread(여러 개) // Thread는 각각 Exception Stack을 별도로 가지고 있는 실행 흐름

 

프로세스 구성 요소 4가지

1. 코드(Code) 영역 : 프로그램의 코드

2. 데이터(Data) 영역 : 전역 변수 or 정적 변수의 할당 공간

3. 힙(Heap) 영역 : 메모리 동적 할당 공간

4. 스택(Stack) 영역  : 지역 변수 할당

 

Thread(스레드)

- 프로그램 내에서 실행되는 프로그램 제어 흐름(실행 단위)을 말한다.

- 스레드는 프로그램 코드를 한 줄씩 실행해준다.

- 스레드마다 독립적인 Stack을 따로 갖는다.

- Single Thread Process(싱글 스레드) : 스레드 1개

- Multi Thread Process(멀티 스레드) : 스레드 2개 이상

- Main Thread(메인 스레드)

 


싱글 스레드 vs 멀티 스레드

- 멀티 스레드는 싱글 스레드보다 효율적이다.

- 스레드의 개수는 제한이 없다.

 

- 스레드가 많을수록 좋은 것은 아니다.

-> 각각 스레드마다 스택이 할당되어야 하기 때문에

-> 스택 영역이 메소드를 호출할 때마다 모든 스레드에도 resource를 주어야 한다.

-> 자원이 부족하기 때문에 효율성 문제가 발생한다.

 

CPU Core(CPU 코어)

- 코어 : CPU 안에서 물리적인 연산(데이터 처리)를 담당하는 곳

- 코어는 한 가지 일만 한다.

- 과거의 코어 개수 1개 -> 최근 코어 개수 2개 이상

 

 

멀티 스레딩(Multi Threading)

- 장점 : 효율적인 처리 , 응답 속도 빠름

- 단점 : 어려운 프로그램, 공유 작업을 위한 동기화 필요 -> 교착상태(DeadLock)

- 동시성(Concurrency), 병렬성(Parallelism) 

- 코어보다 스레드 개수가 더 많을 경우, 우선순위 방식의 영향이 크지 않다.

 

동시성

- 단일 코어 내 멀티 스레드가 교차하면서 여러 Task를 수행함으로써, 동시에 실행되는 것 처럼 보인다.

- 많은 클라이언트의 요청을 처리할 수 있다.

 

병렬성

- 멀티코어 + 멀티 스레드 

- 같은 시간의 2개 이상의 일을 여러 프로세스가 동시에 수행하는 것

- 병렬적 수행을 빠르게 끝낼 수 있다.

 

동시성(Concurrency) 병렬성(Parallelism)
Single Core + 멀티 스레드 + Time-Slicing(시분할) Multi Core + 멀티 스레드
동시에 실행되는 것 처럼 보인다. 실제로 동시에 처리된다.
Context-Switching  
스레드 스케줄링 사용  

- 스레드 스케줄링 : 스레드를 어떤 순서에 의해 동시성으로 실행할 것인지 결정하는 것

- 스레드 스케줄링에 의해, 스레드는 번갈아가면서 run() 메소드 실행

- Java에서 우선순위(Priority), 순환 할당(Round-Robin) 스케줄링 사용

- Priority Scheduling : 우선순위가 높은 스레드 부터, 개발자가 코드로 제어 가능 // setPriority() 메소드로 우선순위 변경 가능

- Round-Robin Scheduling : Time Slice(시간 할당량)을 정해진 시간만큼 실행, JVM에 의해 제어

 

 

멀티 태스킹(Multi Tasking)

- 2가지 이상의 작업을 동시에 처리하는 것

- 멀티 태스킹 != 멀티 프로세스

- 하나의 프로세스도 멀티 태스킹이 가능하다 -> 멀티 스레드 이용

 

 

프로세스와 스레드의 개념

- 하나의 프로세스에서 오류가 발생해도 다른 프로세스에 영향 X

- 멀티 스레드에서 하나의 스레드에 오류가 발생하면 -> 프로세스가 종료될 수 있어 다른 스레드에 영향을 끼칠 수 있다.

- 스레드는 메소드를 실행하면서 JVM에 의해 생성된다.

- 자바에서 작업 스레드는 객체로 생성되기 때문에, Class가 필요하다.

- Thread class : main() 이외의 메소드. 메소드를 이용하여 Thread의 흐름을 제어하기 위한 클래스

- main() 메소드를 수행하기 위한 목적으로 제공되고, main Thread(또는 Daemon Thread)라고 불린다.

메인 스레드(Main Thread)

- main() 메소드를 실행하면서 시작된다.

- 작업 스레드들을 만들어서 병렬로 코드 실행 가능-> 멀티 스레드 생성 -> 멀티 태스킹 수행


Daemon Thread(데몬 스레드)

- 다른 일반 스레드의 보조적인 작업을 위해 사용

- 데몬 스레드는 (데몬)스레드를 파생시킨 일반 Thread가 모두 종료되면 같이 종료된다. 

- 사용 예시 : 가비지 컬렉터(GC), 자동 저장 기능..

- Thread.setDeamon(true) // 데몬 스레드 지정

- start() 호출 후에, setDeamon(true)를 호출하면 -> IllegalThreadStateException 발생

 


멀티 코어 프로세스 문제점

- 항상 메모리에 접근해서 값을 가져오는 것 보다, Core마다 독립적인 캐시 공간에 값을 저장해 두고 사용해서 접근 속도 최적화

- 하지만, 메모리 공간에 있는 값(실제 값)이 바뀌어도 캐시를 통해서 변경 전 값들을 가져오는 문제가 발생할 수 있다.

- volatile 키워드 사용 : 캐시에 저장된 값 X -> main memory 값 사용


스레드 그룹(Thread Group) 

- JVM 실행 시, system 스레드 그룹을 만들고 JVM 운영에 필요한 스레드들을 생성해서 system 스레드 그룹에 포함시킨다.

- 기본적으로 자신을 생성한 스레드와 같은 스레드 그룹에 속하게 된다.

실행 컨텍스트(execution context)

- 코드 실행 순서(세부 정보)를 스택으로 관리하는 것

- 선언된 변수, 함수, 스코프, this에 바인딩 된 값 관리

- 종류

1. global execution Context : 스크립트가 처음으로 실행될 때 생성 -> 스택의 가장 밑

2. function execution Context : 함수가 호출될 때 생성

 

 

렉시컬 환경(Lexical Environment)

- 식별자, 식별자에 바인딩된 값, 상위 스코프에 대한 참조를 담고 있는 자료구조

- 코드를 실행하는데 필요한 정보를 담고 있다.

  • 렉시컬 환경은 언제 가비지 컬렉터의 대상이 되는지?
    • 어떤 식별자도 참조하고 있지 않는 상황에서 메모리를 동적으로 해제한다.

 

 

클로저(Closure) 

- 두 개의 함수(중첩함수)로 만들어진 환경으로 이루어진 특별한 객체의 한 종류

- inner function이 outer function의 scope으로 접근할 수 있게 해준다.

- 클로저는 함수 생성 시간에 함수가 생성될 때마다 만들어진다.

- 중첩함수가 외부 함수의 결과값으로 return

- return 되는 함수가 외부 함수의 식별자를 참조(중첩함수의 lifecycle(생명주기)가 외부 함수보다 길어야 한다.)

- 이때 중첩함수에서 외부 함수에 대한 참조가 남아있기 때문에 외부 함수의 실행을 execution context stack(실행 컨텍스트)에서 제거되지만, 렉시컬 환경은 메모리에 남아있기 때문에 중첩함수에 의해 사용될 수 있는 현상이다.

- 아래 코드의 클로저 : innerFunc

const x = 1;

function outer(){
    const x = 10;

    const inner = function(){
        console.log(x);
    }
    return inner;
}

const innerFunc = outer();
innerFunc();

 

클로저 생성 조건

1. 내부 함수가 익명 함수로 되어 외부 함수의 반환 값으로 사용

2. 내부 함수는 외부 함수의 실행 환경에서 실행

3. 내부 함수에서 사용되는 변수는 외부 함수의 변수 스코프에 있다.

 

 

클로저 활용 예시

const increase = (function(){
    let num = 0;
    return function(){
        return ++num;
    }
}());

console.log(increase());
console.log(increase());
console.log(increase());
// 1 2 3

+ Recent posts