java 멀티 스레드

개미Coder
|2024. 5. 2. 11:15
반응형

 

## 멀티 스레드 개념
 

 

- 대표적인 예시가 카카오톡 메신저이다.

- 싱글 스레드를 사용하게 되면 내가 큰 용량의 이미지를 보낼 때 그 이미지가 보내질 때 까지 메세지를 상대방에게 보낼 수 없다.

- 싱글 스레드는 작업을 하나하나 일일이 순서에 맞게 처리해야 뒤에 순서에 있는 데이터를 그제서야 처리할 수 있다.

 

 

-기존 싱글 스레드에서는 위 for문이 끝나야 아래 for문이 수행되는 방식이다.

-순서대로 *와 #이 찍힌다는 것인데, 멀티 스레드를 사용하면, 이 과정을 동시에 진행한다.

 


 
운영체제에서는 실행 중인 하나의 애플리케이션을 프로세스(process) 라고 부른다. 
사용자가 애플리케이션을 실행하면 운영체제로부터 실행에 필요한 메모리를 할당받아 
애플리케이션의 코드를 실행하는데 이것이 프로세스이다. 
하나의 애플리케이션은 다중 프로세스를 만들기도 하는데, 
예를 들어 Chrome 브라우저를 두 개 실행했다면 두 개의 Chrome 프로세스가 생성된 것이다.

멀티 태스킹 (multi tasking )은 두 가지 이상의 작업을 동시에 처리하는 것을 말하는데,
운영체제는 멀티 태스킹을 할 수 있도록 CPU 및 메모리 지원을 프로세스마다 적절히 할당해주고,
병렬로 실행시킨다. 

예를 들어 워드로 문서 작업을 하면서 동시에 윈도우 미디어 플레이어로 음악을 들을 수 있다. 
멀티 태스킹은 꼭 멀티 프로세스를 뜻하지는 않는다. 
한 프로세스 내에서 멀티 태스킹을 할 수 있도록 만들어진 애플리케이션들도 있다. 
대표적인 것이 미디어 플레이어 (Media player)와 메신저 (Messenger) 이다. 
미디어 플레이어는 동영상 재생과 음악재생이라는 두 작업을 동시에 처리하고,
메신저는 채팅 기능을 제공하면서 동시에 파일 전송 기능을 수행하기도 한다. 
어떻게 하나의 프로세스가 두 가지 이상의 작업을 처리할 수 있을까? 
그 비밀은 멀티 스레드(multithread)에 있다.

스레드(thread)는 사전적 의미로 한 가닥의 실이라는 뜻인데 한 가지 작업을 실행하기 위해 
순차적으로 실행할 코드를 실처럼 이어 놓았다고 해서 유래된 이름이다. 

하나의 스레드는 하나의 코드실행 흐름이기 때문에 한 프로세스 내에 스레드가 두 개라면 
두 개의 코드 실행 흐름이 생긴다는 의미이다. 
멀티 프로세스가 애플리케이션 단위의 멀티 태스킹이라면 
멀티 스레드는 애플리케이션 내부에서의 멀티 태스킹이라고 볼 수 있다. 

다음 그림은 멀티 프로세스와 멀티 스레드의 개념을 보여준다.
                        


멀티 프로세스들은 운영체제에서 할당받은 자신의 메모리를 가지고 실행하기 때문에 서로 독립적이다. 
따라서 하나의 프로세스에서 오류가 발생해도 다른 프로세스에게 영향을 미치지 않는다.  
하지만 멀티 스레드는 하나의 프로세스 내부에 생성되기 때문에 
하나의 스레드가 예외를 발생시키면 프로세스 자체가 종료될 수 있어 다른 스레드에게 영향을 미치게 된다. 

예를 들어 멀티 프로세스인 워드와 엑셀을 동시에 사용하던 도중 
워드에 오류가 생겨 먹통이 되더라도 엑셀은 여전히 사용 가능하다.  
그러나 멀티 스레드로 동작하는 메신저의 경우 파일을 전송하는 스레드에서 예외가 발생되면 
메신저 프로세스 자체가 종료되기 때문에 채팅 스레드도 같이 종료된다. 
그렇기 때문에 멀티 스레드에서는 예외 처리에 만전을 기해야 한다.



# 메인 스레드

-모든 자바 애플리케이션은 메인 스레드(main thread)가 main() 메소드를 실행하면서 시작 된다.
-main() 메소드의 첫 코드부터 아래로 순차적으로 실행

-실행 종료 조건 : 마지막 코드 실행, return문을 만나면

-main 스레드는 작업 스레드들을 만들어 병렬로 코드를 실행

-멀티 스레드 생성해 멀티 태스킹 수행                    

-프로세스의 종료

-싱글 스레드 : 메인 스레드가 종료하면 프로세스도 종료

-멀티 스레드 : 실행 중인 스레드가 하나라도 있다면, 프로세스 미종료

 

 

-멀티 태스킹 : 두 가지 이상의 작업을 동시에 처리하는 것

-멀티 프로세스 : 독립적으로 프로그램들을 실행하고 여러 가지 작업 처리

-멀티 스레드 : 한 개의 프로그램을 실행하고 내부적으로 여러 가지 작업 처리

 

 

 

 

 

## 쓰레드의 구현과 실행



1) Thread 클래스를 상속

-run 메서드 안에 쓰레드가 작업할 내용을 넣어준다. run 메서드를 실행시키는 것이 아닌 start() 메서드를 호출해야 run 안에 있는 메서드들의 내용이 멀티쓰레드로 실행이 된다.

-다른 클래스를 상속받으면 쓰레드 클래스를 상속해서 사용하지 못한다.(다중상속x)

public class MyThread extends Thread {
@Override // Thread 클래스의 run() 메소드를 재정의해야 한다.
poublic void run(){
// 작업 내용
}
}

MyThread thread = new MyThread(); // 쓰레드 생성
thread.start(); // 쓰레드 실행

 

-Thread 클래스를 상속받아서 멀티쓰레드를 사용

-extends Thread 후 run() 메서드를 오버라이딩 한다.

-메인 쓰레드가 먼저 실행 되기에 println이 먼저 끝나는 것을 볼 수 있다.



2) Runnable 인터페이스 구현

 

-Runnable 객체를 만든다.

-쓰레드 클래스로 만드는 매개변수로 runnable을 넣어준다.

-상속되어 있는 클래스가 있으면 Runnable 인터페이스를 구현하면 된다.

public class MyThread2 implements Runnable {
@Override // Runnable 인터페이스의 추상메서드인 run() 메소드를 구현
poublic void run(){
// 작업 내용
}
}
// MyThread2가 run() 메서드를 재정의
Runnable runnable = new MyThread2();   // 인터페이스는 객체를 만들 수 없기때문에 자식 객체를 대입
Thread thread = new Thread(runnable);   // Runnable 객체를 매개변수로 Thread 객체에 대입
thread.start();   // 쓰레드 실행

 

-Runnable interface를 구현하여 멀티스레드 사용

-main 클래스 : new ThreadA는 Runnable에 구현 (상속비슷) 되어있기에 생성자로 호출이 가능하다.

-Thread 클래스에 있는 생성자를 호출해서, 생성자 안에 해당 클래스 변수를 prameter 안에 넣어준다.

 

 

 

 

 

 

#작업 스레드 이름 변경 및 가져오기

 

 

 

-스레드에도 이름을 지정해줄 수 있다. (thread.getName())

 

 

-스레드 이름을 설정해줄 수 있다. (thread.setName())

 

 

-thread.join()을 쓰게 되면 해당 thread의 작업이 종료될 때 까지 대기하고 종료할 수 있다.

(메인 스레드는 해당 tread가 종료되어야 메인 스레드가 종료된다.)

-System.currentTimeMillis()는 1970년 1월 1일 UTC 시간을 기준으로 현재까지의 시간 차이를 밀리 세컨드 단위로 출력한 값이다. 그래서, endTime - startTime을 하면 총 소요시간을 구할 수 있는 것이다.

 

 

 

# I/O Blocking

두 쓰레드가 서로 다른 자원을 사용하는 작업의 경우에는 싱글쓰레드 프로세스보다 멀티쓰레드 프로세스가 더 효율적이다. 
예를 들면 사용자로부터 데이터를 입력받는 작업, 네트워크로 파일을 주고받는 작업, 
프린터로 파일을 출력하는 작업과 같이 외부기기와 입출력을 필요로 하는 경우가 이에 해당한다.

만일 사용자로부터 입력받는 작업(A)과 화면에 출력하는 작업(B)을 하나의 쓰레드로 처리한다고 하면 
사용자가 입력을 마칠 때까지 아무 일도 하지 못하고 기다리기만 해야한다. 
그러나 두개의 쓰레드로 처리한다면 사용자의 입력을 기다리는 동안 다른 쓰레드가 작업을 처리할 수 있기 때문에 
보다 효율적인 CPU의 사용이 가능하다.

쓰레드가 입/출력(I/O)처리를 위해 기다리는 것을 I/O blocking이라고 한다.

 

-Scanner를 사용할 때 Scanner를 입력하지 않으면, 밑에 실행내용이 실행안되고 되기 상태인 것을 보고 I/O blocking이라고 한다.

 

-Thread.sleep(1000) : 시간 딜레이 만들어서 결과값 나오게 하기

-한 마디로 스레드를 잠재우는 기능 (1000 = 1초)

 

 

 

 

-클래스를 통해 Thread를 만들어서, 클래스 안에서 자체적으로 오버라이딩 해서 메서드로 출력하기

-실행시키면, 아이디를 입력하세요(메인 클래스)가 뜸과 동시에 thread.start 메서드를 통해 카운트도 동시에 시작된다.

-Kakao talk 같은 경우에서 저 오버라이딩 기능을 통해 이미지를 보내는 기능을 만들면, 나는 상대방과 톡을 할 수 있음과 동시에 이미지 전송이 가능하다.




# 쓰레드 우선순위

-작업의 중요도에 따라 쓰레드의 우선순위를 다르게 하여 특정 쓰레드가 더 많은 작업을 갖게 할 수 있다.

멀티 쓰레드는 동시성 (Concurrency) 또는 병렬성 (Parallelism ) 으로 실행되기 때문에 
이 용어들에 대해 정확히 이해하는 것이 좋다. 

- 동시성 : 멀티 작업을 위해 하나의 코어에서 멀티 쓰레드가 번갈아가며 실행하는 성질
- 병렬성 : 멀티 작업을 위해 멀티 코어에서 개별 쓰레드를 동시에 실행하는 성질


싱글 코어 CPU를 이용한 멀티 쓰레드 작업은 병렬적으로 실행되는 것처럼 보이지만,
사실은 번갈아 가며 실행하는 동시성 작업이다.  
번갈아 실행하는 것이 워닥 빠르다보니 병렬성으로 보일 뿐이다.

쓰레드의 개수가 코어의 수보다 많을 경우, 쓰레드를 어떤 순서에 의해 동시성으로 실행할 것인가를 결정해야 하는데,
이것을 쓰레드 스케줄링이라고 한다. 
쓰레드 스케줄링에 의해 쓰레드들은 아주 짧은 시간에 번갈아가면서 그들의 run() 메소드를 조금씩 실행한다.

자바의 쓰레드 스케줄링은 우선순위 (Priority)방식과 순환 할당(Round- Robin) 방식을 사용한다. 
우선순위 방식은 우선순위가 높은 쓰레드가 실행 상태를 더 많이 가지도록 스케줄링히는 것을 말한다. 
순환 할당 방식은 시간 할딩량(Time Slice)을 정해서 하나의 쓰레드를 정해진 시간만큼 실행하고 
다시 다른 쓰레드를 실행하는 방식을 말한다.


쓰레드 우선순위 방식은 쓰레드 객체에 우선순위 변호를 부여할 수 있기 때문에 개발자가 코드로 제어할 수 있다. 
하지만 순환할당 방식은 자바 JVM에 의해서 정해지기 때문에 코드로 제어할 수 없다.
우선순위 방식에서 우선순위는 1에서부터 10까지 부여되는데, 1이 가장 우선순위가 낮고, 10이가장 높다. 
우선순위를 부여하지 않으면 모든 쓰레드들은 기본적으로 5의 우선순위를 할당받는다. 
만약 우선순위를 변경하고 싶다면 Thread 클래스가 제공히는 setPriority() 메소드를 이용하면된다.

thread.setPriority(우선순위); // 우선순위값 적용
thread.setPriority(); // 우선순위값 확인



순위가 높은 쓰레드가 실행 기회를 더 많이 가지기 때문에 우선순위가 낮은 쓰레드보다 계산 작업을 빨리 끝낸다.
쿼드 코어일 경우에는 4개의 쓰레드가 병렬성으로 실행될 수 있기 때문에 
4개 이하의 쓰레드를 실행할 경우에는 우선순위 방식이 크게 영향을 미치지 못한다. 
최소한 5개 이상의 쓰레드가 실행되어야 우선순위의 영향을 받는다. 

 

 

-기존 스레드를 getPriority() 메서드를 사용해서 우선순위를 보면 5가 할당 되어있다.

 

 

-기존 스레드들의 우선순위가 5였던 것을 A는 7, B는 4로 우선순위에 차등을 주고, 결과값을 보면 대체적으로 A가 더 빨리 끝나는데, B가 빨리 끝날 때도 있다.(이론적으로는 A가 먼저 끝나는 것이 당연하지만, OS 스케줄러에 Java 우선순위에 따른 스레드 스케줄링에 영향이 가기 때문에 B가 더 빨리 끝날 때도 있다.)

-토끼와 거북이를 보면 알다시피, 누가 이길지 모른다. 스레드의 우선순위도 그렇다.

#스레드 스케줄링

 

-thread1과 thread2의 작업이 끝나는 기준은 OS의 스케줄러가 기준이 된다.

(JVM에서 우선순위를 두었음에도 불구하고, OS안에 있는 프로세서 이기 때문에, 누가 먼저 끝날지는 아무도 모른다.)

-이론은 이런데, Java쪽에서 OS에 우선순위에 따라서 이걸 좀 더 빠르게 끝내달라고 요청해놓은 부분들 때문에, 실전과 이론은 좀 차이가 있을 수 있다.

 

 

 


## 스레드 상태
 
스레드 객제를 생성하고, start() 메소드를 호출하면 곧바로 스레드가 실행되는 것처럼 보이지만 사실은 실행 대기 상태가 된다.   
실행 대기 상태란 아직 스케줄링이 되지 않아서 실행을 기다리고 있는 상태를 말한다. 
실행 대기 상태에 있는 스레드 중에서 스레드 스케줄링으로 선택된 스레드가  CPU를 점유하고 run() 메소드를 실행한다. 
이때를 실행 (Running) 상태라고 한다. 
실행 상태의 스레드는 run() 메소드를 모두 실행하기 전에 스레드 스케줄링에 의해 다시 실행 대기 상태로 돌아 갈 수 있다. 
그리고 실행 대기 상태에 있는 다른 스레드가 선택되어 실행 상태가 된다. 
이렇게 스레드는 실행 대기 상태와 실행 상태를 번갈아가면서 자신의 run() 메소드를 조금씩 실행한다. 
실행 상태에서 run() 메소드가 종료되면, 더 이상 실행할 코드가 없기 때문에 스레드의 실행은 멈추게된다. 
이 상태를 종료 상태라고 한다.

경우에 따라서 스레드는 실행 상태에서 실행 대기 상태로 가지 않을 수도 있다. 
실행 상태에서 일시 정지 상태로 가기도 히는데, 일시 정지 상태는 스레드가 실행할 수 없는 상태이다.   
일시 정지 상태는 WAITING, TIMED_WAITING, BLOCKED가 있다.
스레드가 다시 실행 상태로 가기 위해서는 일시 정지 상태에서 실행 대기 상태로 가야한다는 것만 알아두자.


 NEW | 스레드가 생성되고 아직 start()가 호출되지 않은 상태

 RUNNABLE | 실행 중 또는 실행 가능 상태

 BLOCKED | 동기화 블록에 의해서 일시정지된 상태(block이 풀릴때까지 기다리는 상태)

 WATING | 스레드의 작업이 종료되지는 않았지만 실행가능하지 않은(Unrunnable)일시정지 상태


 WATING | TIME_WAITING은 일시 정지 시간이 지정된 경우를 의미

 TERMINATED | 스레드의 작업이 종료된 상태

 





 sleep() | 지정된 시간(1000/1초)동안 스레드를 일시 정지 시킨다.
 (static) | 지정한 시간이 지나고 나면 자동적으로 다시 실행 대기상태가 된다.


 join() | 지정된 시간동안 스레드가 실행되도록 한다. 지정된 시간이 지나거나 작업이종료되면 join을 호출한 스레드로 다시 돌아와 실행을 계속한다.

 interrupt() | sleep()이나 join()에 의해 일시 정지 상태인 스레드를 깨워서 실행대기 상태로 만든다. 해당 스레드에서는   Interrupted Exception이 발생함으로써 일시 정지 상태를 벗어나게 된다. 

stop이나 join을 깨우는건 예외처리 Exception으로 깨울 수 있다.

 stop() | 스레드를 즉시 종료 시킨다.

 suspend() |스레드를 일시 정지 시킨다.  resume()을 다시 호출하면 실행 대기 상태가 된다.

 

 resume() |실행중에 자신에게 주어진 실행시간을 다른 스레드에게 양보하고 자신은 실행 대기상태가 된다.
 (static)

 yield() |suspend()에 의해 일시 정지 상태에 있는 스레드를 실행대기 상태로 만든다.




# 주어진 시간동안 일시 정지( sleep() ) = 딜레이 만들기

실행 중인 스레드를 일정 시간 멈추게 하고 싶다면 Thread 클래스의 정적 메소드인 sleep()을 사용하면 된다. 
다음과 같이 Thread.sleep() 메소드를 호출한 스레드는 주어진 시간동안 일시 정지상태가 되고,
주어진 시간이 지나게되면 다시 실행 대기 상태로 돌아간다.

매개값에는 얼마동안 일시 정지 상태로 있을 것인지 밀리세컨드(1/1000) 단위로 시간을 주면 된다.
위와 같이 1000이라는 값을 주면 스레드는 1초가 경과할 동안 일시 정지 상태로 있게 된다. 
일시 정지 상태에서 주어진 시간이 되기 전에 interrupt() 메소드가 호출되면 
InterruptedException이 발생하기 때문에 예외 처리가 필요하다. 
예외를 발생시켜 sleep() , yield()를 깨우는 기능이므로 예외 처리시 catch에서는 아무런 설정을 하지 않아도 된다.
sleep() 사용시 매번 예외처리가 불편하다면 메서드로 만들어서 호출하여 사용할 수 도 있다.

 

 

-main 클래스에서 호출한 sleep는 main 클래스를 잠들게 만든다.

-thread1, thread2가 잠드는 것이 아닌 호출한 영역을 잠들게 하는 것이 요점 포인트다.

-interrupt() 메서드가 호출되면 InterruptedException 오류가 발생되기 때문에 예외 처리를 필수적으로 해주어야 한다!

 

-그래서, sleep를 호출할 때는 Thread를 직접 호출하자. 객체로 만들어서 생긴 변수명으로 사용하면 헷갈린다.

 

-main 클래스에서 static delay 메서드를 만들어서 호출하면 딜레이 시킨 시간만큼 각 메서드들이 끝나게 된다.

-깔끔하게 쓰기 위해서는 static 메서드로 try,catch로 예외를 잡아주고, 메서드를 호출하는게 깔끔해보인다.

 





# interrupt() 메소드

interrupt() 메소드는 스레드가 일시 정지 상태에 있을 때 InterruptedException 예외를 발생시키는 역할을 한다. 
이것을 이용하면 run() 메소드를 정상 종료시킬 수 있다. 

대기상태(WAIGING)인 스레드를 실행대기 상태(RUNNABLE)로 만든다. (sleep(), join(), wait()인 상태를 대기 상태로 전환)

void interrupt() : 스레드의 interrupted 상태를 false에서 true로 변경
boolean isInterripted() : 스레드의 interrupted 상태를 반환 - interrupt 된적이 있는가 없는가? (true = 있다)
static boolean interripted() : 스레드의 interrupted 상태를 반환하고, 상태를 false로 초기화


-interrupt를 바로 실행시키면 true 상태가 되고 isInterrupted가 true가 된다.

 

 

-sleep(10000)를 입력해놓고 main 클래스에서 값 입력 메세지에 따라 값을 입력하면 interrupt가 바로 실행이 되어서, 잠에서 일어나게 된다.








## suspend(), resume(), stop()

suspend() : 스레드를 일시 정지
resume() : suspend()에 의해 일시 정지된 스레드를 실행 대기상태로 만든다.
stop() : 스레드를 즉시 종료 시킨다.

스레드를 생성한다음, start()를 하면 실행 대기상태(줄서기)에 있다가 자기 순서때 실행을 하다가 
작업을 다 못 마치면 뒤로가서 다시 줄을 서게 된다. 
자기 작업이 끝나거나 stop()메서드를 호출하면 스레드가 종료된다. 그리고나서 스레드가 소멸하게 된다.

스레드는 작업을 마칠 때 까지 줄서기와 실행을 반복한다.
그러다가 작업이 끝나면 소멸되던가, stop()메서드를 호출하면 강제로 소멸되는 것이다.

실행중에 suspend() 일시정지라는 메서드를 호출하면, 스레드가 통안에 들어가서 WATING(일시정지)상태가 된다.
resume()을 호출하면 일시 정지 상태에서 벗어나서 다시 나와서 줄을 서게 된다.
즉, resume()과 suspend()는 완전히 반대되는 메서드다.

-suspend()는 스레드를 일시 정지, resume()는 일시 정지된 스레드 실행시킨다, stop()는 스레드 즉시 종료

-stop()된 스레드는 resume 해도 다시 불러와지지 않는다.




## join()

-스레드 자신이 하던 작업을 잠시 멈추고 다른 스레드가 지정된 시간동안 작업을 수행하도록 할 때 join()을 사용한다.

-시간을 지정하지 않으면, 해당 스레드가 작업을 모두 마칠 때까지 기다리게 된다.

-작업 중에 다른 스레드의 작업이 먼저 수행되어야할 필요가 있을 때 join()을 사용한다.
-join()도 interrupt()에 의해 대기상태에서 벗어날 수 있으며, try-catch문으로 감싸야한다.

-sleep()과 다른점은 현재 스레드가 아닌 특정 스레드에 대해 동작한다.
 (sleep()은 static메서드로 현재 실행중인 스레드에 동작)

 

 

 

 

 

 

 

## 쓰레드의 동기화 (Thread Synchronizaion)
 
프로세스는 스레드가 운영체제로부터 자원을 할당받아 소스 코드를 실행하여 데이터를 처리한다.
만약, 싱글 스레드 프로세스라면, 공유 데이터에 단 하나의 스레드만이 접근하므로 문제가 될 것이 없다. 
하지만, 멀티 스레드 프로세스의 경우, 두 개 이상의 스레드가 공유 데이터에 동시에 접근하게 되면 
예상과 벗어난 결과가 타나날 수 있다. 이러한 문제를 해결해 주는 것이 바로 스레드 동기화다.

쓰레드 동기화란 여러 쓰레드가 동일한 리소스를 공유하여 사용하게 되면 서로의 결과에 영향을 주기 때문에 
이러한 일을 방지하기 위해 한 쓰레드가 진행중인 작업을 
다른 쓰레드가 간섭하지 못하도록 막는 것을 '쓰레드 동기화(Synchronization)'라고 한다.


스레드 동기화 : 한 스레드가 진행중인 작업을 다른 스레드가 간섭하지 못하도록 막는것


쓰레드 동기화를 하기 위해서는 임계영역(critical section)과 락(lock)을 사용합니다. 
임계영역으로 설정한 구역은 동시에 리소스를 사용할 수 없는 구역이고,
 락을 획득한 쓰레드에 대해서만 리소스를 사용하도록 하는 방식입니다.

1) 메서드 앞에 synchronized를 붙여서 메서드 전체가 임계 영역으로 설정된다.
쓰레드는 해당 메서드가 호출된 시점부터 해당 메서드가 포함된 객체의 lock을 얻어 
작업을 수행하다가 메서드가 종료되면 lock을 반환한다.

스레드 동기화 : 한 스레드가 진행중인 작업을 다른 스레드가 간섭하지 못하도록 막는것




2) 메서드 내의 코드 일부를 블럭{}으로 감싸고 블럭 앞에 'synchronized(참조변수)'를 붙이는 것이다.
이때 참조변수는 락을 걸고자하는 객체를 참조하는 것이어야 한다.
이 블럭을 synchronized블럭이라고 부르며, 쓰레드가 이 블럭 영역 안으로 들어가면 
객체의 lock을 얻게되고, 블럭을 벗어나면 반납한다.

모든 객체는 lock을 하나씩 가지고 있으며, 해당 객체의 lock을 가지고 있는 쓰레드만 
임계 영역의 코드를 수행할 수 있다. 다른 쓰레드들은 lock을 얻을 때까지 기다린다.

 

 

 

 

 

-synchronized를 메서드 앞에 사용하지 않으면, 금액이 -가 될 때 까지 차감시킨다.

왜냐하면, th1, th2 스레드를 두개를 start를 해놨는데, 그러면 동시에 저 둘이 북치고 장구치고 실행이 되서 갑자기 마이너스 금액이 생길 수가 있는데, synchronized를 쓰게 되면 애초에 쓰레드가 하나밖에 들어올 수 없기 때문에 남은 금액이 -로 차감되는 것을 미연에 방지한다.

 

반응형

'java(2)↗' 카테고리의 다른 글

java 컬렉션 프레임워크(List)  (0) 2024.05.03
java 제네릭  (0) 2024.05.03
java 중첩 클래스  (0) 2024.05.02
java Arrays 클래스, Boxing 박싱, Date, Format  (0) 2024.05.01
java String 클래스  (0) 2024.04.29