복습 - 키워드로 정리
•
API 도큐먼트
•
애노테이션
•
Object 클래스
•
제네릭
멀티 스레드
•
하나의 프로세스가 두 가지 이상의 작업을 처리할 수 있는 이유는 멀티 스레드(Multi Thread)가 있기 때문이다.
•
스레드(Thread)는 코드의 실행 흐름을 말하는데, 프로세스 내에 스레드가 두 개라면 두 개의 코드 실행 흐름이 생긴다는 의미이다.
멀티 프로세스 : 프로그램 단위의 멀티 태스킹
멀티 스레드 : 프로세스 단위의 멀티 태스킹 (프로그램 내부)
=> ex) 카카오톡에서 동영상을 보내는 동안 메세지 전송
Plain Text
복사
•
멀티 프로세스는 서로 독립적이므로 하나의 프로세스에서 오류가 발생해도 다른 프로세스에 영향을 미치지 않지만, 멀티 스레드는 하나의 스레드가 예외를 발생시키면, 프로세스가 종료되므로 다른 스레드에게 영향을 미친다.
멀티 스레드 - 메인 스레드, 작업 스레드 생성과 실행
•
모든 Java 프로그램은 메인 스레드(Main Thread)가 main() 메소드를 실행하면서 시작된다.
•
메인 스레드는 main() 메소드의 첫 코드부터 순차적으로 실행하고 main( ) 메소드의 마지막 코드를 실행하거나 return 문을 만나면 실행을 종료한다.
•
멀티 스레드 환경에서는 실행 중인 스레드가 하나라도 있다면 프로세스가 종료되지 않는다.
•
메인 스레드는 반드시 존재하기 때문에 추가적인 작업 수만큼 스레드를 생성한다.
•
작업 스레드도 객체로 관리하기 때문에 클래스가 필요하다.
◦
Thread 클래스로 직접 객체를 생성하는 방법
◦
하위 클래스를 만들어 생성하는 방법
멀티 스레드 - Thread 클래스로 직접 스레드 생성하기
•
java.lang 패키지에는 Thread 클래스로부터 작업 스레드 객체를 직접 생성하기 위해서는
Runnable 구현 객체를 매개값으로 갖는 생성자를 호출하면 된다.
•
Runnable은 스레드가 작업을 실행할 때 사용하는 인터페이스로, 그 안에는 run( ) 메소드가 정의되어 있다
•
구현 클래스는 run( ) 메소드에 스레드가 실행할 코드를 재정의(오버라이딩)해주면 된다.
•
일반적으로는 명시적인 Runnable 구현 클래스를 작성하지 않고, 익명 구현 객체를 통해 작성하는 방식이 더 많이 사용된다.
package com.thread;
public class ThreadExample1 {
public static void main(String[] args) {
// Thread thread = new Thread(/* Runnable 인터페이스 */);
// Thread thread = new Thread(new Task());
Thread thread = new Thread(new Runnable() {
@Override // => 익명 객체 형태를 더 많이 사용한다.
public void run() {
// 작업 스레드가 처리할 코드
for (int i = 1000; i > 0; i--) {
System.out.println("스레드 " + i);
}
}
});
// 해당 스레드를 시작시킨다.
thread.start();
thread.start();
thread.start();
thread.start();
thread.start();
for (int i = 0; i < 1000; i++) {
System.out.println("메인 " + i);
}
}
}
Java
복사
package com.thread;
public class ThreadExample1 {
public static void main(String[] args) {
// Thread thread = new Thread(/* Runnable 인터페이스 */);
// Thread thread = new Thread(new Task());
Thread thread = new Thread(new Runnable() {
@Override // => 익명 객체 형태를 더 많이 사용한다.
public void run() {
// 작업 스레드가 처리할 코드
for (int i = 5; i > 0; i--) {
System.out.println("스레드 " + i);
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
// 해당 스레드를 시작시킨다.
thread.start();
// 메인 스레드
for (int i = 0; i < 5; i++) {
System.out.println("메인 " + i);
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Java
복사
멀티 스레드 - Thread 자식 클래스로 스레드 생성하기
•
작업 스레드 객체를 생성하는 또 다른 방법은 Thread의 자식 객체로 만드는 것이다.
•
Thread 클래스를 상속한 다음 run( ) 메소드를 재정의해서 스레드가 실행할 코드를 작성하고 객체를 생성하면 된다.
package com.thread;
public class ThreadExample2 {
public static void main(String[] args) {
// Thread thread = new Thread의 자식 클래스();
Thread thread = new WorkerThread();
// 해당 스레드를 시작시킨다.
thread.start();
// 메인 스레드
for (int i = 0; i < 5; i++) {
System.out.println("메인 " + i);
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
// ================
메인 0
스레드 5
스레드 4
메인 1
스레드 3
메인 2
스레드 2
메인 3
스레드 1
메인 4
Java
복사
멀티 스레드 - 스레드 상태
•
스레드 상태는 Thread.State 타입으로 정의되어 있다.
(NEW, RUNNABLE, TERMINATED, TIMED_WAITING, BLOCKED, WAITING)
•
RUNNABLE - 실행과 실행 대기 반복, CPU를 다른 스레드들과 나눠 사용
◦
RUNNABLE 상태에서는 상황에 따라 TIMED_WAITING, BLOCKED, WAITING 상태로
전환될 수 있다.
package com.thread2;
public class ThreadStateExample {
public static void main(String[] args) {
// 스레드의 상태
// NEW : 스레드가 생성된 후, start 전
// RUNNABLE : start 후 실행 준비가 된 상태 (실행 중일 수도.. 실행 대기일 수도..)
// TERMINATED : 실행 종료
Thread.State state;
Thread thread = new Thread() {
@Override
public void run() {
for (int i = 0; i < 1000000000; i++) {
}
}
};
state = thread.getState(); // enum 타입이어서 사용 가능
System.out.println("스레드 상태1: " + state); // 스레드 상태1: NEW
thread.start();
state = thread.getState();
System.out.println("스레드 상태2: " + state); // 스레드 상태2: RUNNABLE
try {
// CPU를 해당 10억번 작업 스레드가 모두 사용하도록 메인 스레드 대기
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
state = thread.getState();
System.out.println("스레드 상태3: " + state); // 스레드 상태3: TERMINATED
}
}
Java
복사
멀티 스레드 - 스레드 일시정지
•
예시 - 다운로드 한 폴더를 여는 과정
package com.thread3;
public class ThreadA extends Thread {
@Override
public void run() {
ThreadB threadB = new ThreadB();
threadB.start();
try { // 스레드 일임을 한다.
threadB.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread.currentThread().setName("폴더 열기");
String name = Thread.currentThread().getName();
System.out.println(name + " 시작");
System.out.println("폴더를 엽니다."); // 다운로드 완료되면
System.out.println(name + " 끝");
}
}
Java
복사
package com.thread3;
public class ThreadB extends Thread {
@Override
public void run() {
Thread.currentThread().setName("다운로드");
String name = Thread.currentThread().getName();
System.out.println(name + " 시작");
for (int i = 0; i <= 100; i += 10) {
System.out.println("다운로드: " + i + "%");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(name + " 끝");
}
}
Java
복사
package com.thread3;
public class ThreadPauseExample {
public static void main(String[] args) {
Thread thread = new Thread(new ThreadA());
thread.start();
}
}
Java
복사
멀티 스레드 - 스레드 양보
•
스레드가 처리하는 작업은 반복처리가 많은 편인데, 가끔은 반복이 무의미한 반복으로 처리되는 경우가 있다.
•
이 때는 다른 스레드에게 실행을 잠시 양보하고, 자신은 실행 대기 상태로 가는 것이 프로그램 성능에 도움을 준다.
•
Thread는 yield( ) 메소드를 제공하여, yield( )를 호출한 스레드는 실행 대시 상태로 돌아가고, 다른 스레드가 실행 상태가 된다.
package com.thread4;
public class ThreadLoop extends Thread {
public boolean work = true;
@Override
public void run() {
while (true) {
if (work) {
System.out.println(Thread.currentThread().getName() + "의 작업처리");
} else {
Thread.yield();
}
}
}
}
Java
복사
package com.thread4;
public class ThreadLoopExample {
public static void main(String[] args) {
ThreadLoop thread0 = new ThreadLoop();
ThreadLoop thread1 = new ThreadLoop();
thread0.setName("첫번째 스레드");
thread1.setName("두번째 스레드");
thread0.start();
thread1.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread0.work = false;
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread0.work = true;
}
}
Java
복사
멀티 스레드 추가 내용
•
스레드 동기화
◦
멀티 스레드는 하나의 객체를 공유해서 작업도 할 수 있다. 하지만 다른 스레드에서 객체를 조작한다면, 의도했던 결과와는 다른 결과를 초래할 수 있다. 스레드가 사용 중인 객체를 다른 스레드가 변경할 수 없도록 객체에 잠금을 걸 수 있다.
◦
동기화 메소드와 동기화 블록은 동시 접근이 불가능하다.
•
wait(), notify()
◦
두 개의 스레드를 가지고 정확히 교대로 번갈아가며 작업하도록 처리할 수 있다.
◦
한 스레드가 작업을 완료하면 notify( ) 메소드를 호출해, 일시정지에 있는 다른 스레드를 실행대기 상태로 만들고, 자신은 두 번 작업을 하지 않도록 wait( ) 메소드를 호출하여 일시정지 상태로 만든다.
◦
주의할 점은 wait(), notify() 메소드 모두 동기화 메소드 또는 동기화 블록 내에서만 사용 가능하다는 것이다.
•
스레드 안전 종료
◦
조건 분기 처리 또는 interrupt( ) 메소드 사용으로 후, 리소스 정리 작업을 추가하는 것으로 스레드를 안전하게 종료할 수 있다.
•
데몬 스레드
◦
데몬 스레드는 주 스레드의 작업을 돕는 보조적인 스레드로, 주 스레드가 종료되면 자동으로 종료된다.
◦
데몬 스레드로 만들기 위해서는 start( ) 호출 전에 setDaemon(true)를 호출하면 된다
자료구조
•
선형 자료구조
◦
데이터가 순차적으로 배열되는 자료구조
◦
배열, ArrayList, LinkedList, Stack, Queue 등등…
◦
List는 연속방식과 연결방식으로 나뉜다.
▪
연속 방식 (메모리 공간 기반) ⇒ 한 번 정해진 길이를 변경할 수 없다!
▪
연결 방식 (포인터_메모리주소번지 기반) - LinkedList
◦
동적 배열의 원리 - 더블링 (시간복잡도 1에 수렴)
▪
미리 초기값을 잡아 배열의 길이를 정해서 생성하고 데이터가 추가되면 늘려주고, 모두 복사해서 만들어준다.
▪
더블링은 진행될 때마다 O(n)이다. 하지만 자주 일어나는 것이 아니므로 O(1)로 간주하고 프로그래밍을 하는 것이 일반적이다.
컬렉션 자료구조
•
자료구조를 바탕으로 객체들을 효율적으로 이용할 수 있도록 관련된 인터페이스와 클래스들을 java.util 패키지에 포함시켜 놓았다.
•
이들을 총칭해서 컬렉션 프레임워크(Collection Framework)라고 부른다.
•
컬렉션 프레임워크는 몇 가지 인터페이스를 통해서 다양한 컬렉션 클래스를 이용할 수 있도록 설계되어 있다.
•
주요 인터페이스로는 List, Set, Map 등이 있다.
컬렉션 자료구조
컬렉션 자료구조 - List 컬렉션
•
객체를 인덱스로 관리하기에 객체를 저장하면 인덱스가 부여되고, 인덱스로 객체를 검색, 삭제할 수 있는 기능을 제공한다.
•
ArrayList
◦
List 컬렉션에서 가장 많이 사용되는 컬렉션이다.
◦
ArrayList는 제한 없이 객체를 추가할 수 있다.
List<E> list = new ArrayList<>() ; // E에 지정된 타입의 객체만 저장
List<E> list = new ArrayList<E>(); // E에 지정된 타입의 객체만 저장
List list = new ArrayList(); // 모든 타입의 객체를 저장
Java
복사
Vector
•
Vector는 ArrayList와 동일한 내부 구조를 가지고 있으며, 동기화된 메소드로 구성되어 있어서 멀티 스레드가 동시에 Vector 메소드를 실행할 수 없다는 점에서 차이가 있다.
•
멀티 스레드 환경에서도 안전하게 객체를 추가, 삭제할 수 있다.
List<E> list = new Vector<E>(); // E에 지정된 타입의 객체만 저장
List<E> list = new Vector<E>(); // E에 지정된 타입의 객체만 저장
List list = new Vector(); // 모든 타입의 객체를 저장
Java
복사
컬렉션 자료구조 - List 컬렉션(LinkedList)
•
LinkedList는 ArrayList와 사용 방법은 동일하지만 내부 구조는 완전히 다르다.
•
ArrayList는 내부 배열에 객체를 저장하지만, LinkedList는 인접 객체를 사슬 형태로 연결해서 관리한다.
List<E> list = new LinkedList<E>(); // E에 지정된 타입의 객체만 저장
List<E> list = new LinkedList<E>(); // E에 지정된 타입의 객체만 저장
List list = new LinkedList(); // 모든 타입의 객체를 저장
Java
복사
•
비교
package com.collection.list;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class LinkedListExample {
public static void main(String[] args) {
// ArrayList 객체 생성 [문자열]
List<String> list1 = new ArrayList<>();
// LinkedList 객체 생성 [문자열]
List<String> list2 = new LinkedList<>();
// 시간 측정을 위한 변수 선언
long startTime;
long endTime;
startTime = System.nanoTime();
for (int i = 0; i < 10000; i++) {
list1.add(0, String.valueOf(i));
}
endTime = System.nanoTime();
System.out.println("추가 시 걸린 시간 (ArrayList): " + (endTime - startTime));
startTime = System.nanoTime();
for (int i = 0; i < 10000; i++) {
list2.add(0, String.valueOf(i));
}
endTime = System.nanoTime();
System.out.println("추가 시 걸린 시간 (LinkedList): " + (endTime - startTime));
startTime = System.nanoTime();
for (int i = 0; i < 10000; i++) {
list1.get(i);
}
endTime = System.nanoTime();
System.out.println("조회 시 걸린 시간 (ArrayList) : " + (endTime - startTime));
startTime = System.nanoTime();
for (int i = 0; i < 10000; i++) {
list2.get(i);
}
endTime = System.nanoTime();
System.out.println("조회 시 걸린 시간 (LinkedList) : " + (endTime - startTime));
}
}
// ==========================================
추가 시 걸린 시간 (ArrayList): 5314333
추가 시 걸린 시간 (LinkedList): 961125
조회 시 걸린 시간 (ArrayList) : 296125
조회 시 걸린 시간 (LinkedList) : 47712083
Java
복사
컬렉션 자료구조 - Set 컬렉션
•
List 컬렉션은 저장 순서를 유지하지만, Set 컬렉션은 저장 순서가 유지되지 않는다.
또한 객체를 중복해서 저장할 수 없고, 하나의 null만 저장할 수 있다.
•
Set 컬렉션은 수학의 집합에 비유될 수 있다.
•
Set 컬렉션은 순서가 없기 때문에 인덱스로 관리하지 않는다. 즉, 인덱스를 매개값으로 갖는 메소드가 없다.
•
Set 컬렉션에는 HashSet, LinkedHashSet, TreeSet 등이 있는데,
Set 컬렉션에서 공통적으로 사용 가능한 Set 인터페이스 메소드는 아래와 같다.
HashSet
•
Set 컬렉션 중에서 가장 많이 사용되는 것이 HashSet이다.
•
HashSet은 hashCode( ) 메소드의 리턴값이 같고, equals( ) 메소드가 true를 반환하면 동일한 객체로 판단하고 중복 저장하지 않는다.
package com.collection.list;
public class Member {
private String name;
private int age;
public Member(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
return name.hashCode() + age;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Member) {
Member m = (Member) obj;
return m.name.equals(name) && m.age == age;
}
return false;
}
}
// ==========================================
package com.collection.list;
import java.util.HashSet;
public class SetExample2 {
public static void main(String[] args) {
// HashSet 컬렉션 객체 생성
HashSet<Member> hashSet = new HashSet<>();
// 동일한 데이터 입력
hashSet.add(new Member("minsung", 26));
hashSet.add(new Member("minsung", 26));
// 저장된 객체 수를 출력
System.out.println("저장된 객체 수 : " + hashSet.size());
}
}
// 저장된 객체 수 : 1
Java
복사
package com.collection.list;
import java.util.HashSet;
import java.util.Set;
public class SetExample {
public static void main(String[] args) {
// HashSet 컬렉션 객체 생성
Set<String> set = new HashSet<>();
// 컬렉션 객체에 데이터 저장 - 순서 X
set.add("Java");
set.add("JDBC");
set.add("JSP");
set.add("JPA");
set.add("Servlet");
set.add("MyBatis");
set.add("Java");
System.out.println("저장된 요소의 개수 : " + set.size());
System.out.println(set);
}
}
Java
복사
TreeSet
•
이진 트리(Binary Tree)를 기반으로 검색 기능을 강화한 Set 컬렉션이다.
•
이진 트리는 여러 개의 노드(node)가 트리 형태로 연결된 구조로, 루트 노드라고 불리는 하나의 노드에서 시작해 각 노드에 최대 2개의 노드를 연결할 수 있는 구조를 가지고 있다.
•
TreeSet에 저장되는 객체는 저장과 동시에 오름차순으로 정렬된다.
(낮은 것은 왼쪽 자식 노드에, 높은 것은 오른쪽 자식 노드에 저장)
•
어떤 객체든지 오름차순으로 정렬될 수 있는 것은 아니고, Comparable 인터페이스를 구현하고 있는 객체만이 정렬 가능하다.
•
Set 타입 변수에 대입해도 되지만, TreeSet 타입으로 대입한 이유는 검색 관련 메소드가 TreeSet에만 정의되어 있기 때문이다.
TreeSet<E> treeSet = new TreeSet<E>();
TreeSet<E> treeSet = new TreeSet<>();
Java
복사
Map
•
Map 컬렉션은 키(Key)와 값(Value)으로 구성된 엔트리(Entry) 객체를 저장한다.
•
키와 값은 모두 객체이고, 키는 중복 저장할 수 없지만 값은 중복 저장할 수 있다는 특징이 있다.
•
만약 동일한 키로 값을 저장하면, 기존의 값이 새로운 값으로 대치된다.
컬렉션 자료구조 - Map 컬렉션(HashMap)
•
HashMap 은 키로 사용할 객체가 hashCode() 메소드의 리턴값이 같고, equals( ) 메소드가 true를 반환하는 경우, 동일 키로 간주하고 중복 저장을 허용하지 않는다.
package com.collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class MapExample1 {
public static void main(String[] args) {
// 이름과 나이를 가지고, HashMap 컬렉션 생성
Map<String, Integer> map = new HashMap<>();
map.put("김연아", 33);
map.put("지민", 8);
map.put("전지현", 42);
map.put("박보검", 30);
map.put("손흥민", 32);
map.put("지민", 28); // 덮어쓴다.
// 요소의 총 개수
System.out.println("총 entry 수 : " + map.size());
System.out.println(map);
// 키를 통해 값 얻기
Integer sonAge = map.get("손흥민");
System.out.println("손흥민의 나이 : " + sonAge);
// 키로 구성된 set를 구해서, 반복문을 통해 값만 구하기
Set<String> keySet = map.keySet();
Iterator<String> iterator = keySet.iterator();
while (iterator.hasNext()) {
String key = iterator.next();
System.out.println(key);
}
}
}
Java
복사