-
221220 자바 13 (스레드, 람다) : 스레드스타터스 백엔드 3기 2022. 12. 20. 15:02
하루입니다.
컬렉션 프레임워크와 api 복습!


generics 복습!

제네릭 사용 안 할 때 / 제네릭 사용할 때
스레드
- 프로그램 : 컴퓨터 cpu가 실행할 수 있는 이해 가능한 이진코드의 집합체
- 프로세스 : 현재 cpu가 실행중인 프로그램
- window / mac / linux : 1번에여러개 프로그램 동시실행 가능
- OS가 멀티프로세스 환경(멀티 프로세싱 방식)을 지원한다.
- cmd 같은 게 단일 프로세싱 방식
스레드
- 실타래! 실타래가 얽히며 무언가 만들어내다!
- 싱글스레드
- 멀티스레드 : 1번에 여러개의 스레드를 동시에 실행할 수 있는 환경.
- 1개 cpu 3개 시간 분할 실행 방식 + 우선순위
- 자바웹서버 - 멀티스레드 환경 구현
- 자바 웹서버에서 사용하는 라이브러리들은 이미 멀티스레드가 구현되어 있다.
- 한 개의 스레드로 두 번 start() 할 수 없다.
멀티스레드 구현 방식
java.lang.Thread 클래스 java.lang.Runnable 인터페이스 다중상속 가능한 특징 사용한다.
class A extends Thread {
@override
public void run() {
다른 작업과 동시에 수행할 멀티스레드 수행 내용
}
}class A extends Thread B, implement Runnable {
@override
public void run() {
다른 작업과 동시에 수행할 멀티스레드 수행 내용
}
}
A a1 = new A();
a1.start();Runnable r1 = new A();
Thread t1 = new Thread(r1);
t1.start();
1. Thread 상속
2. run() 오버라이딩
3. Thread 객체 생성
4. start() 호출1. Runnable 상속(구현)
2. run() 오버라이딩
3. Runnable 객체 생성
4. Thread 객체 변환
5. start() 호출class MyThread extends Thread { @Override public void run() { for(int i = 0; i <= 5; i++) { System.out.println("a" + i); } } } class MyThread2 implements Runnable { @Override public void run() { for(int i = 0; i <= 5; i++) { System.out.println("b" + i); } } } public class ThreadTest1 { // main 스레드 (자바 자동 실행) public static void main(String[] args) { MyThread mt = new MyThread(); MyThread2 mt2 = new MyThread2(); Thread t2 = new Thread(mt2); mt.start(); // mt2.start(); mt2는 Thread 상속받지 않아서 .start(); 사용 불가하다. t2.start(); System.out.println("main 종료"); } } ---------------------------------------------------------------------------------- main 종료 b0 b1 a0 b2 a1 a2 a3 a4 a5 b3 b4 b5run(); 이 아닌 start();를 사용하는 이유
- run()을 호출하는 것은 클래스에 선언된 메소드를 호출하는 것이다. 멀티쓰레드를 호출하는 게 아니다.
- 호출스택에서 main() 위에 run()이 올라가는 형태가 되어 run이 종료되어야 main이 종료되게 된다.
- start()를 호출하면 새로운 스레드를 생성하여 스레드가 사용할 호출스택을 생성하고, 새로운 호출스택에서 run()이 호출되어 스레드가 독립된 공간에서 작업을 수행할 수 있고, 이제 호출스택이 2개이므로 스케줄러가 정한 순서에 의해 번갈아가며 실행된다.
- main이 종료되어도 다른 쓰레드의 작업이 끝나지 않았다면 프로그램도 종료되지 않는다.
쓰레드의 I/O 블락킹
- 원래대로라면 INPUT창이 사라지기 전까지 FOR문은 실행되지 않았겠지만, 멀티스레드로 실행하기에 for문이 실행되면서 input창에서 입력도 받을 수 있다.
class NumberThread extends Thread { @Override public void run() { for(int i = 0; i <= 10; i++) { System.out.println(i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class ThreadTest3 { public static void main(String[] args) { NumberThread nt = new NumberThread(); nt.start(); String input = JOptionPane.showInputDialog("값 입력해 보세요"); System.out.println("입력값 = "+ input); } } ------------------------------------------------------------------------- 0 1 2 입력값 = 1 3 4 5 6 7 8 9 10스레드 상태도
생성상태
new Thread()실행 가능 상태
start()
t1()
t2()
자바 스레드 스케줄러
스레드 실행순서 순위
1. 우선순위가 높다
기본으로 스레드는 우선순위 5를 가진다.
숫자가 클 수록 우선순위가 높다.
t1.setPriority(10);
t1.getPriority(); ==> 10
2. time slice 방법 - 시간을 일정하게 분할하기
OS 종류마다 다르다.
윈도우는 우선순위 무시, 일정한 시간 분배실행상태
run()일시중지상태
sleep(1/1000초)
join()
동기화 상태
wait설정한 시간이 경과하면 다시 실행된다.
join된 스레드의 실행이 완료된 후 다시 실행된다.
t1.run{ t2.join() }종료상태 
멀티스레드의 우선순위
- setPriority를 이용하여 우선순위를 부여할 수 있다.
- getPriority를 이용하여 우선순위를 조회할 수 있다.
- 기본 우선순위는 5이다.
- 숫자가 클 수록 우선순위가 높다. 범위는 1 ~ 10이다.
- 우선순위가 있다고 해서 꼭 더 많은 실행시간과 실행기회가 주어진다고 볼 수는 없다. 아래 코드를 보면 a(i)를 출력하는 스레드에 우선순위 10을 주었음에도 b(i)와 섞여 나오는 것을 알 수 있다.
- main 메소드의 우선순위는 Thread.currentThread().getPriority() 방식으로 조회할 수 있다.
class MyThread extends Thread { @Override public void run() { for(int i = 0; i <= 50; i++) { System.out.println("a" + i); } } } class MyThread2 implements Runnable { @Override public void run() { for(int i = 0; i <= 50; i++) { System.out.println("b" + i); } } } public class ThreadTest1 { public static void main(String[] args) { MyThread mt = new MyThread(); MyThread2 mt2 = new MyThread2(); Thread t2 = new Thread(mt2); mt.setPriority(10); t2.setPriority(1); System.out.println("mt 우선순위 : " + mt.getPriority()); System.out.println("m2 우선순위 : " + t2.getPriority()); System.out.println("main 우선순위 : " + Thread.currentThread().getPriority()); mt.start(); t2.start(); System.out.println("main 종료"); } } ----------------------------------------------------------------------- mt 우선순위 : 10 m2 우선순위 : 1 main 우선순위 : 5 main 종료 a0 a1 a2 b0 b1 b2 a3 a4 b3 a5 ...
일시중지상태
sleep(1/1000초)
- 지정된 시간 동안 스레드를 멈추게 한다.
- 지정된 시간이 다 되거나, interrupt()가 호출될 때 깨어나 실행대기 상태가 된다.
package chap13; import javax.swing.JOptionPane; class NumThread extends Thread { NumThread(String name) { super(name); // public Thread(String name) 생성자 호출, 이름으로 간주하게 된다. } @Override public void run() { for(int i = 0; i <= 10; i++) { System.out.println(getName() + " : " + i); try { Thread.sleep(1000); // 1초씩 쉬니까 쉬는 동안 다른 스레드가 진행된다. } catch (InterruptedException e) { e.printStackTrace(); } } } } public class ThreadTest4 { public static void main(String[] args) { NumThread nt1 = new NumThread("nt1"); NumThread nt2 = new NumThread("nt2"); nt1.start(); nt2.start(); } } --------------------------------------------------------------- nt1 : 0 nt2 : 0 nt1 : 1 nt2 : 1 nt2 : 2 nt1 : 2 ... nt1 : 10 nt2 : 10join()
- 작업을 멈추고 다른 스레드가 작업을 수행할 동안 기다린다.
- 예외처리 필수이다.
package chap13; class JoinThread extends Thread { int i = 1; @Override public void run() { i = 10; System.out.println("run 메소드 내부 : " + i); } } public class ThreadTest5 { public static void main(String[] args) { JoinThread t = new JoinThread(); t.start(); // != run. 바로 run 안 될 수도 있다. runable 상태이다. // t: 실행가능상태, main: 실행상태 try { t.join();// t: 실행상태, main: 일시중단상태 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("main 메소드 내부 : " + t.i); // 1 or 10 System.out.println("main 종료"); } } ------------------------------------------------------------------------ run 메소드 내부 : 10 main 메소드 내부 : 10 main 종료yield
- 자신에게 주어진 실행시간을 다음 차례의 스레드에게 양보한다.
- 만약 1초를 할당받은 쓰레드가 0.5초동안 작업한 상태에서 yield()가 호출되면, 나머지 0.5초는 포기하고 다시 실행대기 상태가 된다.
스레드의 동기화 (synchronization)
- 여러개 스레드의 접근을 제어한다.
- 한 스레드가 진행중인 작업을 다른 스레드가 간섭하지 못하도록 하는 것을 쓰레드의 동기화(synchronization)라고 한다.
- 여러 쓰레드가 같은 프로세스 내의 자원을 공유해서 작업하기에 서로의 작업에 영향을 줄 수도 있다.그래서 한 스레드가 작업을 끝마치기 전까지 다른 스레드에 의해 방해받지 않도록 하기 위한 개념이 쓰레드의 동기화이다.
- 남발시 스레드 수행 속도가 저하된다.
- synchronized를 메소드앞에 붙이면 된다.
- 멀티스레드가 동작하는 환경에서 멀티스레드들이 객체를 공유한다.
- 공유 객체들이 순서에 따라 접근한다.
- 공유 객체에 lock을 설정한다.
- 열쇠 가진 스레드1가 접근하면 lock이 해제된다. 다른 스레드2는 접근이 불가해서 대기해야 한다.
- 스레드1 수행이 완료되고 나가면 다시 lock이 설정된다.
- 스레드 2가 lock을 해제하고, 수행 완료되고 나가면 다시 lock이 설정된다.
- 공유 객체가 실행되는 동안 단일스레드로 동작한다.
동기화 이전 코드와 결과
- 시간이 분할되어 수행되기에 이상한 결과가 나온다.
- 한번에 하나의 스레드만 수행하도록 해야 한다.
class Data { int value; void saveValue(int value) { this.value += value; System.out.println( Thread.currentThread().getName() +" 스레드가 " + value + " 전달 후 "+ this.value); } } class DateThread1 extends Thread{ Data data; int value; public DateThread1(String name, Data data, int value) { super(name); this.data = data; this.value = value; } @Override public void run() { data.saveValue(value); } } public class ThreadTest6 { public static void main(String[] args) { Data data = new Data(); // data를 DateThread1과 DateThread2가 공유하고 있다. DateThread1 dt1 = new DateThread1("dt1", data, 100); // name, data, value DateThread1 dt2 = new DateThread1("dt2", data, 200); DateThread1 dt11 = new DateThread1("dt11", data, 300); DateThread1 dt22 = new DateThread1("dt22", data, 400); dt1.start(); dt2.start(); dt11.start(); dt22.start(); } } ------------------------------------------------------------------------------- dt1 스레드가 100 전달 후 600 dt22 스레드가 400 전달 후 1000 dt11 스레드가 300 전달 후 600 dt2 스레드가 200 전달 후 600동기화 이후 코드와 결과
class Data { int value; // 아래 메소드 앞에 synchronized 를 붙였다. synchronized void saveValue(int value) { this.value += value; System.out.println( Thread.currentThread().getName() +" 스레드가 " + value + " 전달 후 "+ this.value); } } --------------------------------------------------------------- dt1 스레드가 100 전달 후 100 dt22 스레드가 400 전달 후 500 dt11 스레드가 300 전달 후 800 dt2 스레드가 200 전달 후 1000wait(); notify();
- 동기화된 임계 영역의 코드를 수행하다가 작업을 더이상 진행할 상황이 아니면, 일단 wait()를 반환하여 쓰레드가 lock을 반환하고 기다리게 한다. 다른 쓰레드가 lock을 얻어 해당 객체에 대한 작업을 수행하도록 한다. 나중에 작업 진행 가능한 상황이 오면 notify()를 호출하고, 다시 lock을 얻어 진행 가능하도록 한다.
- 하나의 스레드는 저장만 하고, 하나의 스레드는 읽기만 한다. 두 번씩 실행되면 안 된다. 1 저장 1 조회 2 저장 2 조회 이렇게 돼야 한다. 1 저장 2 저장 2 조회 이건 안 된다.
- 같은 객체 영역에 접근하는 스레드이다.
- 스레드간 서로 통신할 필요가 있다. 나 1 저장했어! 나는 1 조회할게!
wait(); notify(); 사용 전 결과


wait(); notify(); 사용 후 결과


ThreadTest1 ThreadTest2 ThreadTest3 ThreadTest4(sleep) ThreadTest5(JOIN) ThreadTest6(동기화) ThreadTest7(wait, notify)
'스타터스 백엔드 3기' 카테고리의 다른 글
221221 자바 14. 입출력 (0) 2022.12.21 221220 자바 13 (스레드, 람다) : 람다식이 뭘까요? (0) 2022.12.20 221219. 자바 12. 제네릭 <> (0) 2022.12.19 221219 자바 11. 컬렉션 프레임워크 (List, Set, Map) (0) 2022.12.19 유데미 스타터스 취업 부트캠프 3기 - 백엔드 4주차 (0) 2022.12.16