Object의 wait()/notify()

18513 단어 object
wait (), notify (), notify All () 는 Object 클래스에 정의된 세 가지 방법으로 라인의 상태를 제어할 수 있습니다.
이 세 가지 방법은 최종적으로 jvm급의native 방법을 사용한다.jvm 운영 플랫폼에 따라 약간의 차이가 있을 수 있습니다.
• 대상이wait 방법을 사용하면 대상의 라인을 가지고 그 대상의 제어권을 넘겨주고 기다리는 상태가 된다.
• 대상이 notify 방법을 사용하면 이 대상의 제어권을 기다리고 있는 라인이 계속 실행될 수 있음을 알립니다.
• 대상이 notifyAll 방법을 호출하면 이 대상의 제어권을 기다리는 모든 라인이 계속 실행될 것을 알립니다.
- 대상의 통제권은 사실 우리가 말한 모든 대상이 가지고 있는 파이프라인(Monitor)(개인은 파이프라인으로 번역하는 것이 비교적 좋다고 생각하는데 JSR133 중국어 버전에 근거함)은 잠길 수 있고 잠금의 입도는 현재의 라인이지 어떤 조작이 아니다.
그 중에서wait방법은 세 가지overload방법이 있는데 그것이 바로wait()wait(long)wait(long,int)이다.
wait 방법은 매개 변수를 통해 기다리는 시간을 지정할 수 있습니다.매개 변수를 지정하지 않으면 기본적으로 알림을 받을 때까지 기다립니다.
다음은 복잡한 문제를 가장 간결하게 설명하는 프레젠테이션 코드입니다.
1. Notify Thread는 3초 후에 다른 대기 상태를 알리는 루틴을 모의하는 루틴 종류이다.
2. WaitThread는 기다리는 라인 종류를 모의하는 데 사용된다.
3. 기다림의 중간 대상은 flag, 하나의 String 대상이다.
main 방법에서 Notify 스레드와 세 개의wait 스레드를 동시에 시작합니다.
package concurrent;



public class NotifyTest {



    private String flag = "true";



    class NotifyThread extends Thread {



        public NotifyThread(String name) {



            super(name);



        }



        public void run() {



            try {



                sleep(3000);//   3    



            } catch (InterruptedException e) {



                e.printStackTrace();



            }



            flag = "false";



            flag.notify();



        }



    };



    class WaitThread extends Thread {



        public WaitThread(String name) {



            super(name);



        }



        public void run() {



            while (flag != "false") {



                System.out.println(getName() + " begin waiting!");



                long waitTime = System.currentTimeMillis();



                try {



                    flag.wait();



                } catch (InterruptedException e) {



                    e.printStackTrace();



                }



                waitTime = System.currentTimeMillis() - waitTime;



                System.out.println("wait time :" + waitTime);



            }



            System.out.println(getName() + " end waiting!");



        }



    }



    public static void main(String[] args) throws InterruptedException {



        System.out.println("Main Thread Run!");



        NotifyTest test = new NotifyTest();



        NotifyThread notifyThread = test.new NotifyThread("notify01");



        WaitThread waitThread01 = test.new WaitThread("waiter01");



        WaitThread waitThread02 = test.new WaitThread("waiter02");



        WaitThread waitThread03 = test.new WaitThread("waiter03");



        notifyThread.start();



        waitThread01.start();



        waitThread02.start();



        waitThread03.start();



    }



}

OK, 이 프로그램을 가지고 실행하면 전혀 실행되지 않을 거야, what happened?화면이 가득한java.lang.IllegalMonitorStateException.
맞아, 이 프로그램에는 많은 문제가 있어, 우리 하나씩 보자.우선, 여기서 주의해야 할 몇 가지 사실은:
1. 어느 때든지 대상의 제어권(monitor)은 한 라인에만 있을 수 있다.
2. 실행 대상의wait, notify, notifyAll 방법이든 현재 실행 중인 라인이 대상의 제어권을 얻었음을 보장해야 한다(monitor)
3. 제어권이 없는 라인에서 대상을 집행하는 상기 세 가지 방법은java에 보고한다.lang.Illegal Monitor State Exception 예외.
4. JVM은 다중 스레드를 기반으로 하며 기본적으로 런타임 스레드의 시차성을 보장할 수 없습니다.
상기 몇 가지 사실에 근거하여 우리는 라인이 대상의 통제권을 가지도록 확보해야 한다.즉, waitThread에서 wait 방법을 실행할 때 waitThread가 flag에 대한 통제권을 확보해야 한다.notify Thread에서 notify 방법을 실행할 때, notify Thread가 flag에 대한 제어권을 확보해야 합니다.
스레드를 제어할 수 있는 방법은 세 가지가 있습니다.
1. 실행 대상의 동기화 실례 방법.
2. 대상이 대응하는 클래스의 동기화 정적 방법을 실행한다.
3. 객체에 동기화 잠금을 설정한 동기화 블록을 실행합니다.
위의 세 가지 방법은 모두synchronized와 관련이 있는 것 같습니다.
우리는 세 번째 방법으로 설명한다.
1. 위의 notify 및 wait 메서드를 동기화 블록에 포함합니다.
class NotifyThread extends Thread {



        public NotifyThread(String name) {



            super(name);



        }



        public void run() {



            try {



                sleep(3000);//   3    



            } catch (InterruptedException e) {



                e.printStackTrace();



            }



            synchronized(flag){

                flag = "false";



                flag.notify();

            }

            



        }



    };



    class WaitThread extends Thread {



        public WaitThread(String name) {



            super(name);



        }



        public void run() {

            

            synchronized (flag) {

                while (flag != "false") {



                    System.out.println(getName() + " begin waiting!");



                    long waitTime = System.currentTimeMillis();



                    try {



                        flag.wait();



                    } catch (InterruptedException e) {



                        e.printStackTrace();



                    }



                    waitTime = System.currentTimeMillis() - waitTime;



                    System.out.println("wait time :" + waitTime);



                }

            }



            System.out.println(getName() + " end waiting!");



        }



    }

우리는 한 걸음 앞으로 나아갔다.문제가 해결되었습니까?
실행 중인지 자바를 잘못 보고한 것 같습니다.lang.IllegalMonitorStateException.what happened?
이 때의 이상은 flag 대상에 대한 동기화 블록에서 flag 대상의 상태를 변경했기 때문입니다.flag="false"
flag.notify();
동기화 블록에서 flag에 값을 부여하는 작업을 진행하여 flag가 인용하는 대상을 바꾸었습니다. 이때 notify 방법을 다시 호출할 때 제어권이 없기 때문에 이상을 던집니다.flag을 자바빈으로 바꾸고 속성을 변경하면 flag의 인용에 영향을 주지 않습니다.우리가 이곳에서 수조로 바꾸어 시험해 봐도 같은 효과에 도달할 수 있다.
private   String flag[] = {"true"};

이때 다시 운행하고 이상 보고를 하지 않지만 라인이 끝나지 않았죠. 맞아요. 그리고 라인이 막혀wait상태에 있어요.이유는 매우 간단합니다. 우리는 세 개의wait라인이 있습니다. 단지 하나의 notify라인만 있습니다. notify라인이 notify방법을 실행할 때, 기다리고 있는 라인을 무작위로 알립니다. 그래서 현재는 두 개의 라인이waiting에 있을 것입니다.Notify Thread 스레드 클래스의 flag만 추가하면 됩니다.notify () 방법을 notify All () 로 바꾸면 됩니다.notifyAll 메서드는 객체 제어권을 기다리는 모든 스레드를 알려줍니다.최종 완성판은 다음과 같습니다.
package concurrent;



public class NotifyTest {



    private String[] flag = {"true"};



    class NotifyThread extends Thread {



        public NotifyThread(String name) {



            super(name);



        }



        public void run() {



            try {



                sleep(3000);//   3    



            } catch (InterruptedException e) {



                e.printStackTrace();



            }



            synchronized(flag){

                flag[0] = "false";



                flag.notifyAll();

            }

            



        }



    };



    class WaitThread extends Thread {



        public WaitThread(String name) {



            super(name);



        }



        public void run() {

            

            synchronized (flag) {

                while (flag[0] != "false") {



                    System.out.println(getName() + " begin waiting!");



                    long waitTime = System.currentTimeMillis();



                    try {



                        flag.wait();



                    } catch (InterruptedException e) {



                        e.printStackTrace();



                    }



                    waitTime = System.currentTimeMillis() - waitTime;



                    System.out.println("wait time :" + waitTime);



                }

            }



            System.out.println(getName() + " end waiting!");



        }



    }



    public static void main(String[] args) throws InterruptedException {



        System.out.println("Main Thread Run!");



        NotifyTest test = new NotifyTest();



        NotifyThread notifyThread = test.new NotifyThread("notify01");



        WaitThread waitThread01 = test.new WaitThread("waiter01");



        WaitThread waitThread02 = test.new WaitThread("waiter02");



        WaitThread waitThread03 = test.new WaitThread("waiter03");



        notifyThread.start();



        waitThread01.start();



        waitThread02.start();



        waitThread03.start();



    }



}

다른 블로그에 게시된 기사를 추가합니다.
다중 스레드의 경우 같은 프로세스의 여러 스레드가 같은 저장 공간을 공유하기 때문에 편리함을 가져오는 동시에 방문 충돌이라는 심각한 문제를 가져왔다.자바 언어는 이러한 충돌을 해결하기 위해 전문적인 메커니즘을 제공하여 같은 데이터 대상이 여러 라인에 동시에 접근하는 것을 효과적으로 피한다.
wait와 notify는 자바 동기화 메커니즘의 중요한 구성 부분이다.synchronized 키워드와 결합하여 사용하면 많은 우수한 동기화 모델을 구축할 수 있다.
synchronized(this) {}는
public synchronized void method(){.....}

동기화는 클래스 레벨과 대상 레벨로 나뉘는데 각각 클래스 자물쇠와 대상 자물쇠에 대응한다.클래스 자물쇠는 클래스마다 하나뿐입니다.static 방법이synchronized 키워드에 수식되면 이 방법이 실행되기 전에 클래스 자물쇠를 획득해야 합니다.대상 자물쇠가 유사하다.
우선, Object의 wait와 notify/notify All을 호출할 때, 호출 코드가 이 Object에 동기화되는 것을 확보해야 한다. 즉,synchronized (obj) 와 같은 역할을 해야 한다.내부적으로만 obj의wait와 notify/notifyAll 세 가지 방법을 호출할 수 있습니다. 그렇지 않으면 오류가 발생합니다.
  java.lang.Illegal Monitor State Exception:current thread not owner는wait를 호출할 때 라인이 점유한 대상 자물쇠를 자동으로 방출하고 대상 자물쇠를 신청하지 않습니다.라인이 깨어났을 때, 라인은 비로소 대상 자물쇠를 얻을 권리를 다시 얻었다.
따라서 notify와 notify All은 별 차이가 없다. 단지 notify는 하나의 라인만 깨우고 자물쇠를 얻을 수 있다. notify All은 이 대상을 기다리는 모든 라인을 깨우고 대상 자물쇠를 얻을 수 있다. synchronied 블록에 있는 코드만 깨우면 대상 자물쇠가 없으면 한 발자국도 못 간다.사실 한 라인을 깨우는 것은 그 라인이 대상 자물쇠를 얻고 아래로 운행하도록 다시 허용하는 것이다.
notify All, 모든 wait의 대상에 대해 notify를 한 번씩 호출하지만, 이것은 순서가 있습니다. 모든 대상은 이 대기 대상 체인을 저장합니다. 호출의 순서는 이 체인의 순서입니다.사실 시작 대기 대상 체인 중의 각 라인도 하나의 라인이므로 구체적으로 응용할 때 주의해야 한다.
wait(), notify(), notifyAll()는 Thread 클래스가 아닌 Object 베이스 클래스로, 각 쌍에 wait(), notify(), notifyAll() 기능이 있다는 뜻이다.모든 이미지에 자물쇠가 있기 때문에 자물쇠는 모든 이미지의 기초이다. 물론 자물쇠를 조작하는 방법도 가장 기초이다.
wait (): 대상의 동기화 자물쇠를 기다리려면 대상의 동기화 자물쇠를 가져와야 합니다. 그렇지 않으면 컴파일링을 통과할 수 있지만 실행할 때 이상이 발생합니다: Illegal Monitor State Exception.
임의의 대상의wait () 방법을 호출하면 이 라인이 막히고, 이 라인은 계속 실행할 수 없으며, 대상의 자물쇠가 풀립니다.
notify (): 이 대상이 동기화되기를 기다리는 라인을 깨웁니다. (하나만 깨웁니다. 여러 개가 기다리면) 이 방법을 호출할 때, 대기 상태를 정확하게 깨울 수 없습니다. JVM에서 어떤 라인을 깨울지, 우선순위가 아닙니다.
임의의 대상을 호출하는 notify () 방법은 대상의wait () 방법을 호출하여 막힌 라인에서 무작위로 선택한 차단을 해제합니다. (그러나 자물쇠를 얻은 후에야 실행할 수 있습니다.)
notify All (): 모든 대기 라인을 깨웁니다. 깨운 라인은 notify 이전의wait 라인입니다. notify 이후의wait 라인은 효과가 없습니다.
일반적으로 다선정 간에 조율 작업이 필요하다. 조건이 충족되지 않으면 기다린다.조건이 충족되면, 이 조건을 기다리는 라인이 깨어납니다.자바에서 이 메커니즘의 실현은wait/notify에 의존한다.대기 메커니즘과 잠금 메커니즘은 밀접하게 관련되어 있다.
예를 들면 다음과 같습니다.
synchronized(obj) {

while(!condition) {

obj.wait();

}

obj.doSomething();

}

스레드 A가 obj 자물쇠를 획득한 후 조건condition이 만족하지 않아 다음 처리를 계속할 수 없음을 발견하여 스레드 A는wait()입니다.다른 스레드 B에서 B가 특정 조건을 변경하여 스레드 A의 condition 조건이 충족되면 스레드 A를 깨울 수 있습니다.
synchronized(obj) {

condition = true;

obj.notify();

}

주의해야 할 개념은 다음과 같다. #obj의wait(), notify() 방법을 호출하기 전에obj 자물쇠를 획득해야 한다. 즉synchronized(obj) {...}에 써야 한다.코드 세그먼트 내. #obj를 호출합니다.wait () 후, 스레드 A는 obj의 자물쇠를 방출합니다. 그렇지 않으면 스레드 B는 obj 자물쇠를 얻을 수 없고,synchronized (obj) {...}코드 세그먼트에서 깨우기 A. #obj.wait() 방법이 반환되면 스레드 A는 obj 자물쇠를 다시 가져와야 계속 실행할 수 있습니다. #만약 A1, A2, A3이 모두 obj에 있다면.wait(), B는 obj를 호출합니다.notify () 는 A1, A2, A3 중 하나만 깨울 수 있습니다. (구체적으로 어느 것은 JVM에 의해 결정됩니다.) #obj.notifyAll () 는 A1, A2, A3을 모두 깨울 수 있지만, obj를 계속 실행해야 합니다.wait()의 다음 문장은 반드시 obj 자물쇠를 얻어야 하기 때문에 A1, A2, A3은 자물쇠를 얻어 계속 실행할 기회가 한 개만 있다. 예를 들어 A1은 A1이obj 자물쇠를 풀고 나서야 계속 실행할 수 있다. #B가 obj를 호출하면notify/notify All 때 B가 obj 자물쇠를 가지고 있었기 때문에 A1, A2, A3는 깨어났지만 obj 자물쇠를 얻지 못했다.B가 synchronized 블록을 종료하고 obj 자물쇠를 풀면 A1, A2, A3 중 하나가 자물쇠를 획득하여 계속 실행할 수 있습니다.
synchronized와wait(), notify() 등의 관계에 대해 이야기합니다.
1.synchronized가 있는 곳에 반드시 wait, notify가 있는 것은 아니다
2.wait,notify가 있는 곳에는synchronized가 있어야 한다.이것은wait와notify가 라인류에 속하는 것이 아니라 모든 대상이 가지고 있는 방법이기 때문이다. 그리고 이 두 가지 방법은 모두 대상 자물쇠와 관련이 있고 자물쇠가 있는 곳에는synchronized가 있기 때문이다.
또한 주의사항: notify와wait 방법을 함께 사용하려면 notify를 먼저 호출한 후에wait를 호출해야 합니다.wait를 호출하면 이 라인은currentthread가 아니기 때문입니다.

좋은 웹페이지 즐겨찾기