자바 쓰레드 동기화 기본개념!!
계속 쓰레드 공부를 미뤄오다가 최근에 쓰레드에 대해서 공부하게 되었습니다.
근데 동기화에서 계속 개념이 헷깔려서 고수님들의 도움을 얻고자 이렇게 질문을 남깁니다. 아놔 원래 질문 진짜 안하는데;;;
'락' 이라는 개념에 대해서 입니다.
자바에서 동기화를 시킬때 synchronized 키워드를 씁니다.
제가 알기론 this를 날리면 this 객체에 대한 락을 가지지 않은 쓰레드는 this 객체에 접근을 못한다고 알고 있습니다.
비록 synchronized(this){ }의 블록이 함수 내에서 사용되더라도 그 블록 안에서 쓰레드가 돌고 있다면
'다른 쓰레드는 인자로 날린 객체에 접근을 못한다' 라고 알고 있습니다.
근데 제가 테스트 해본결과 다른 쓰레드가 this 객체의 함수를 호출 가능했습니다. 젠장!!
main(){
ATM atm = new ATM(); //ATM 클래스는 Runnable를 인터페이스로 가집니다
ATM2 atm2 = new ATM2(atm); //ATM2 도 마찬가지구요. 대신 atm객체를 받아왔습니다.
Thread mother = new Thread(atm,"mother");
Thread son = new Thread(atm2,"son");
mother.start();
son.start();
}
A쓰레드를 만들어서 run()안에 synchronized(this)를 선언하고 그 안에 for룹을 돌렸습니다.(여기의 this는 atm이죠)
B쓰레드를 만들어서 atm2 객체에 A가 쓰는 atm을 넘겨주고 run() 함수안에서 atm 이 가지고 있는 일반함수를 호출했습니다.
그럼 호출이 안돼어야 하는것 아닙니까?
왜 A쓰레드의 for 룹으로 메시지가 나오는 중간에 B 쓰레드에서 atm의 함수를 통해 호출한 메시지가 나오는 겁니까
분명 A는 atm의 락을 가지고 있을테고 그럼 B는 atm 객체를 사용 못해야 정상이 아닙니까?
여기서 제가 테스트한 코드의 전문입니다.
[SynchronizedEx.java]
public class SynchronizedEx{
public static void main(String[] ar){
ATM atm = new ATM();
ATM2 atm2 = new ATM2(atm);
Thread mother = new Thread(atm,"mother");
Thread son = new Thread(atm2,"son");
mother.start();
son.start();
}
}
[ATM.Java]
class ATM implements Runnable{
private long deposit_Money = 10000; //잔액
public void run(){
synchronized(this){
for(int i = 0 ; i < 100 ; i++){
if(getDepositMoney() <= 0)
break;
withDraw(100);
}
}
}
public void withDraw(long lv_howmuch){
if(getDepositMoney() > 0){
deposit_Money -= lv_howmuch;
System.out.println(Thread.currentThread().getName() + ",");
System.out.printf("잔액 : %, d 원 %n",getDepositMoney());
}else{
System.out.print(Thread.currentThread().getName() + "," );
System.out.println("잔액이 부족합니다");
}
}
public long getDepositMoney(){
return deposit_Money;
}
public void printTest(){
System.out.println("I am Used");
}
}
class ATM2 implements Runnable{
ATM atm;
ATM2(ATM atm){
this.atm = atm;
}
public void run(){
//synchronized(this){
atm.printTest();
//}
}
}
A쓰레드가 돈을 계산하면서 9000원 8900원 8800원... 이렇게 찍고 있는 도중에
B쓰레드에서 A가 락을 건 atm클래스의 printTest()를 통해 호출한 I am used 라는 메시지가 찍힙니다.
돌아버리겠습니다.
그래서 추측한것이 '아, synchronized(this)의 의미는 인자로 넘긴 객체 자체에 락을 거는게 아니구나. 그냥 그 블록 안에 있는 코드만 접근 못하게 하는구나' 라고 생각했습니다.
근데 그렇게 생각하면 synchronized(this) 도 있고 synchronized(다른 오브젝트) 도 있는데
왜 이렇게 구분해 놓았을까요. 그냥 this 넘기고 블록씌우면 끝나는 얘긴데 인자로 오브젝트가 들어간다는 말은
그 오브젝트에 대해 뭔가를 작업 하니까 그런거라고 생각합니다. 도대체 인자로 넘기는 객체에 뭔일을 하는겁니까?
미쳐버리겠습니다.
어떤 책에는 함수 자체에.. 예를 들어 void synchronized AAA(){ } 요렇게 하면 함수 자체에 락을 거는거라고하면서
synchronized(this)와 동일하다는 말도 있고.. synchronized(어떤 오브젝트)와는 다르다는데 머가 다른지도 모르겠고.
오브젝트에 락을 걸었다고 생각하는데 다른 쓰레드에서 그 오브젝트에 버젓이 접근가능하고(물론 synchronized 블록이 있는 곳은 접근 못했지만 블록 밖의 코드는 접근 가능한것 같더라구요.)
글이 길었는데 답답해서 그랬습니다. 조언을 부탁드리겠습니다. ㅠㅠ
일단 답답하신
일단 답답하신 마음은 이해가 가나, 질문을 하실 때는 좀 더 정제된 표현이 필요합니다.
문제의 현상, 의심되는 원인, 설명이 필요한 부분 등이 섞여 있으면 읽기가 힘듭니다.
그러면 당연히 답변을 받을 확률도 낮아지겠지요 :)
1. synchronized(lock){ A } 와 synchronized(lock){ B } 의 A, B 는 동시에 수행되지 않습니다.
2. synchronized(lock){ A } 와 synchronized(lock'){ B } 는 동시에 수행될 수 있습니다.
3. synchronized(lock){ A } 와 B 는 동시에 수행될 수 있습니다.
한마디로 말씀드리자면 동일한 lock 을 잡는 synchronized 블록들 끼리만 동기화 됩니다.
또한 메써드 synchronized 키워드는 synchronized(this){ 메써드 내용 } 과 동일합니다.
따라서 public synchronized void doA(){ A } 와 synchronized(this){ B } 의 A, B 는 동시에 수행되지 않습니다.
ATM2 는 run 메써드 안의 주석처리된 synchronzied(this){} 를 comment out 하시더라도 원하시는 동작을 하지 않습니다.
이유는 ATM2 의 this 는 Thread son 이고 ATM 의 this 는 Thread mother 이기 때문입니다.
--
말할 수 있는 것은 분명하게 말해질 수 있다;
말해질 수 없는 것에 대해서는 침묵해야한다.
논리철학논고 - 루드비히 비트겐슈타인
--
말할 수 있는 것은 분명하게 말해질 수 있다;
말해질 수 없는 것에 대해서는 침묵해야한다.
논리철학논고 - 루드비히 비트겐슈타인
아 그게아니고..
우선 글을 손가는대로 쓰는게 습관이 되어 두서없이 질문을 올리게 된것에 대해 사과드립니다;;
다시 제 질문의 요지를 말씀드리자면,
'A라는 쓰레드가 Lock을 건 객체를 B라는 쓰레드가 Lock이 걸린 객체의 동기화 블록외의 코드를 접근 가능한가?' 입니다.
synchronized(this)로 가정하자면(ATM클래스에 있는 코드라면 this는 ATM이 되겠죠) A쓰레드가 이 코드를 실행한다면 this 클래스의 락은 A쓰레드가 가지게 됩니다.
A쓰레드가 this 객체의 동기화 코드를 수행 중 일때 B 쓰레드가 this 객체의 동기화 블록 밖에 있는 코드를 수행 가능한가라는 겁니다. 즉, B가 ATM의 동기화 블록 밖의 코드를 접근할 수 있을까 라는게 질문의 요지였습니다.
저는 객체에 락을 건 쓰레드 외에는 다른 쓰레드가 그 객체에 접근이 불가능하다고 배웠고 제가 테스트한 코드는 다른 쓰레드가 그 객체의 동기화된 블록은 접근하지 못했지만 그 객체의 다른 함수는 접근했습니다.
3번의 답이 그런의미로 쓰신건지요?
마지막에 주석처리된 synch(this) 코드는 두 객체를 동기화 시키려는 목적이 아니었고 다른 테스트 도중 남은 코드입니다.
저걸 지우고 올렸어야 했는데 괜한 오해만 불러왔네요.
그리고 또 하나 궁금한건 ATM2의 this가 Thread son이고 ATM의 this가 Thread mother 객체 입니까?
ATM2의 this는 ATM2가 아닙니까? synchronized 함수를 사용하면 그런식으로 this가 바뀌는 건지 궁금합니다.
머.. 제가 답변을 제대로 이해 못한것인지 모르겠지만 제 질문이 제대로 전달되지 않은것 같아서 다시 정리해서 질문드립니다.
endofhope 님이 답을
endofhope 님이 답을 정확히 말씀드리신 것 같습니다.
'A라는 쓰레드가 Lock을 건 객체를 B라는 쓰레드가 Lock이 걸린 객체의 동기화 블록외의 코드를 접근 가능한가?'
=> 당연히 가능합니다. synchronized 블록이 걸려있지 않다면 동기화에 전혀 영향을 미치지 않죠.
ATM2의 this는 ATM2가 아닙니까? synchronized 함수를 사용하면 그런식으로 this가 바뀌는 건지 궁금합니다.
=> 해당 문맥에서는 atm2 가 맞습니다. endofhope 님이 착각하신 것 같습니다.
만약에 위의 코드가 atm 의 synchronized 블록의 수행동안 atm2에서 atm.printTest() 로의 진입을 막고 싶으신 것이 목적이라면.. ATM2 의 코드가...
이 되야겠죠.. 그러면 atm의 synchronized(this) 에서의 this 와 atm2의 synchronized(atm) 에서의 atm 은 같은 객체이기 때문에 락이 걸려서 두 코드는 동시에 수행되지 않게 됩니다.
--
Emerging the World!
Emerging the World!
아 그렇군요
synchronized의 인자로 넘어가는 객체에 락이 걸린다는 말은 객체 전체에 락이 걸린다는 말은 아니었군요.
제가 찾아본 자료들에서는 전부 '락이 걸린 객체에 접근 불가하다'라는 식으로 써놓아서 마치 다른 쓰레드는 그 객체를 아예 못쓴다는 뉘앙스를 많이 받아 이렇게 질문해 본겁니다. 제가 쓴 코드에서도 접근 가능했었구요.
여튼 이렇게 알려주셔서 감사합니다 ^^
죄송한 부탁이지만 이참에 모니터에 대해서 조금 물어봐도 될까요?
어떤 책에선 모니터를 '락에 의해 보호받는 루틴'이라고 표시하고 어떤 사이트에서는 락을 가진 쓰레드를 제외한 쓰레드의 접근을 막는 어떤 메커니즘처럼 얘기를 합니다. 락과 모니터는 모든 Object에 붙어있다는 식으로 얘기를 하는데 명확히 무엇인지는 알기가 어려워서 혹시 아시나 싶어서 질문드려봅니다.
상호 배제 (mutual
상호 배제 (mutual exclusion) 을 지원하는 것을 monitor 라고 합니다.
이러한 monitor 의 구현은 Mutex, Semaphore, Conditional variable 등으로 할 수 있으며,
Java 에서의 synchronized 블록은 이러한 Monitor 의 구현체라고 하겠습니다.
모든 Object 에 붙어있다 는 아니고 Java 에서 모든 Object 에 붙일 수 있다 가 좀 더 적절한 표현입니다.
(이 말은 primitive 데이터와 null 에는 못 붙인다는 말입니다.)
--
말할 수 있는 것은 분명하게 말해질 수 있다;
말해질 수 없는 것에 대해서는 침묵해야한다.
논리철학논고 - 루드비히 비트겐슈타인
--
말할 수 있는 것은 분명하게 말해질 수 있다;
말해질 수 없는 것에 대해서는 침묵해야한다.
논리철학논고 - 루드비히 비트겐슈타인
눈쌀 찌푸리게 하는
눈쌀 찌푸리게 하는 글 제목이군요.
읽어 보기도 싫다는.
그렇군요
눈쌀 찌푸리는 제목이라서 다시 바꿨습니다.
이제 한번 읽어보시죠.
그냥 지나가다가.
원글님이 뭐라고 제목을 적으셨는지는 모르겠지만,
님의 글은 상당히 눈쌀 찌푸리게 하는군요.
댓글 달기