본문 바로가기
자바 , 기타 공부/자바 기초.1.04까지만

쓰레드, 프로세스

by 임지혁코딩 2023. 12. 28.

프로그램은 실행 가능한 파일을 의미하고? 프로세스는 실행중인 프로그램을 의미한다.

 

프로세스는 자원(resource)와 쓰레드로 구성되어있다. 

쓰레드는. 프로세스 네에서 실제 작업을 수행하는 것이다

 

즉, 개념상으로 이해하자면 프로세스라는 공장에서(실제 가동중) 일꾼들이 일을 하고, 이 일꾼들을 쓰레드라고 한다.

멀티 태스킹은 동시에 여러 프로세스를 실행시키는 것을 의미한다. 

멀티 쓰레딩은 하나의 프로세스에서 여러 쓰레드를 실행시키는 것으로  , 생성 비용이 적고 쓰레드들은 자원을 공유한다.

멀티 ㅡ레딩은 자원이 효율적으로 사용되고, 응답성도 좋아진다

허나! 동기화에 주의해야하고 , dead-lock상태가 발생할 수 있다. 즉 주의해야할 사항들이 생긴다. 

 

1. Thread를 상속받아서 사용하거나, 2.Runabble 인터페이스를 구현함으로써 쓰레드를 구현할 수 있다.

 

 

MyTrhead t1 = new MyThread(); t1.start();로 쓰레드를 실행할 수 있다.

start는 main 위 layer에서. run을 main과 별개의 layer에서 작동한다. 

 

실제 구현 모습을 보면, 싱글 쓰레드는 한개의 쓰레드에서 여러가지 동작(for문 2개 등)을 담당하고

멀티 쓰레드는 Thread를 상속받아 2개의 class로 구현후 , 각 객체를 생성한다. (각 쓰레드끼리 겹침을 주의해야 한다)

싱글 코어는 순차 실행되지만, 병행이 가능하고. 멀티 쓰레드의 경우에는 병행이 기본이고 병렬적으로도 가능하다. 

 

blocking이란, 막힘으로 입출력시 작업이 중단되는 것을 의미하는데, 멀티쓰레드는 이 구간동안 다른일을한다.

(어, A야 너 쉴거야? 그러면 B돌려) 당연하다.

우선순위를 지정해줄 수도 있다. 

 

데몬쓰레드 라는 개념이 있는데, 일반 쓰레드의 작업을 돕는 보조역할을 수행하고 . 일반쓰레드가 종료되면 자동 종료된다.

가비지 컬랙터, 자동저장, 화면갱신등에 사용된다. 

 

실행을 제어하는 다양한 메서드들이 있다. joiin()-> 지정시간 동안 실행 , sleep, stop등이 그 예시이다. 

NEW는 아직 start가 호출되지 않은, runnable은 실행중 or 실행 가능한, blocked는 일시정지된 상태이다 .

interrupt()->대기중인 쓰레드를 실행대기(runable)로 만든다. 

stop(),resume()등 정지 시키는 메서드가 있는데, 이는 교착상태에 빠지기 쉬우므로 주의해야한다.

해당 함수들은 deprecated(기억나는가? 더는 사용하지 않겠다는 어노테이션이 있었다) 되었다.

직접 구현이 필요하다. 

yield()는 남은 시간을 다음 쓰레드에게 양보하고 자신은 실행대기한다.

join()은 일정시간동안 특정 쓰레드가 동작하게끔 한다. 

 

동기화

- 한번에 하나의 쓰레드만 객체에 접근할수있게 객체에 락을 거는 것. 

동시에 여러가지 쓰레드가 접근하는 문제를 해결할 수 있는 부드러운 수단이다. 

wait()는 객체의 lock해제, notify()대기중 쓰레드를 꺠운다 , notifyAll()모든 쓰레드를 꺠운다. 

 

총 EX)

요리사가 table에 음식을 추가하고, 손님은 그 음식을 먹는다.

요리사도, 손님도 같은 객체 table을 공유하므로, 동기화가 필요하다. 

요리사 객체가 table을 사용하고(요리해서 올린다) 그때 손님이 그걸 먹어버린다. 

or table에 요리가 1개있는데, 2개의 손님 객체가 이를 먹는다. 

그렇다면 자바에서 error가 발생한다. 

Table의 add와 remove를 구현할때 변경이 필요!

변경된 예시이다. syncronized됨을 명시하였으며, 현재 남아있는 size가 없다면 이를 먹기위해 접근한 thread 를 sleep상태로 변경시킨다. 

허나 이렇게 변경하면, 먹지 못한 손님이 계속해서 대기해버린다. 

그이후 dishes가 추가되면, 기다리고 있는 thread를 notify()하는 기능을 추가하여 손님이 다시 접근할 수 있게끔 할 수 있다. 

 

Lock을 거는 방법엔 

java.util.concurrent.locks패키지에서 제공하는 재진입가능한 ReentrantLock, 읽기엔 공유적인 ReentrantReadWriteLock , 재진입가능 + 낙관적 lock 인 StampedLock기능이 추가되었다 ( JDK 1.8부터) 

 

이와 같이 사용한다. 이러한 방식을 일단 LOCK을 걸고, 그 이후에 확인하는 낙관적 LOCK이라고 한다.

ReentrantLock을 활용하여, synchronized 대신 lock과 unlock을 사용할 수 있따. 

synchronized(lock)에서, lock.lock()으로 더 쉽게 표현 가능하다. 

 

ReentrantLock과 Condition으로 쓰레드를 구분해서 wait & notify 가 가능하다. 

 

violatile boolena suspended = false;

violatile은 , cahce와 메모리간에 불일치를 해소한다. core의 chche에 변수의 값을 저장해놓고 작업하는데,

violatile을 붙여줘야 메모리에서 읽어와서 값의 불일치를 해소할 수 있다. 

 

fork & join

작업을 쓰레드들이 나눠서 쉽게 처리하게 해준다. 

RecursiveAction, RecursiveTask(action은 반환값이 없을떄, task는 있을때 사용)

작업을 나눠서 다른 쓰레드의 작업 큐에 넣는것을, 작업 훔치기 라고 한다. 

fork는 해당 작업을 쓰레드의 작업큐에 넣고, join은 기다린 후 그 결과를 반환

 

 

---총정리 ---

thread들이 접근한다. (프로세스내에서 일하는 일꾼) 

thread들이 접근하는 객체에 lock을걸고. (lock.lock() or syncrhonized, Thread.wait()등)

후에 notify시켜 다시 접근하게 한다. 

 

쓰레드의 개념을 실습에 사용해보는 예시가 온다면, 반드시 복습해야될 내용. (안정적인 서비스 유지에 필수적)