Java5에는 이미 Java.util.concurrent 패키지에 읽기 및 쓰기 잠금 장치가 포함되어 있습니다. 그럼에도 불구하고, 우리는 그 구현의 원칙을 이해해야합니다.
읽기/쓰기 잠금의 Java 구현
먼저 리소스에 대한 액세스를 읽고 쓰는 조건에 대한 개요를 제공합시다.
읽기 스레드가 쓰기 작업을 수행하지 않으며 스레드가 쓰기 작업을 요청하지 않습니다.
스레드가 읽기 및 쓰기 작업을 수행하지 않습니다.
스레드가 리소스를 읽고 싶어하는 경우, 스레드가 리소스에 쓰지 않는 한, 스레드가 리소스에 쓰는 것을 요청하지 않는 한. 우리는 쓰기 작업 요청이 읽기 작업 요청보다 더 중요하다고 가정하므로 쓰기 요청의 우선 순위를 높여야합니다. 또한 읽기 작업이 자주 발생하고 쓰기 작업의 우선 순위를 높이 지 않으면 "기아"가 발생합니다. 쓰기 작업을 요청하는 스레드는 모든 읽기 스레드가 readWritelock에서 잠금 해제 될 때까지 차단됩니다. 새 스레드의 읽기 작동 권한이 항상 보장되면 쓰기 작업을 기다리는 스레드가 계속 차단되며 결과는 "기아"가됩니다. 따라서 읽기 작업은 쓰기 작업을 위해 READWRITELOCK을 잠그는 경우에만 계속 보장 할 수 있으며 스레드는 잠금을 쓰기 작업에 준비하도록 요청하지 않습니다.
다른 스레드가 공유 리소스에서 작업을 읽거나 쓰지 않으면 스레드는 공유 리소스에 대한 쓰기 잠금을 얻은 다음 공유 리소스에 작업을 쓸 수 있습니다. Writ
위의 설명에 따르면, 읽기/쓰기 잠금 장치가 간단히 구현되며 코드는 다음과 같습니다.
공개 클래스 readwritelock {private int readers = 0; 개인 int Writers = 0; 개인 int writeRequests = 0; public synchronized void lockread ()는 InterruptedException {while (writers> 0 || writeRequests> 0) {wait (); } 독자 ++; } public synchronized void unlockread () {리더-; notifyall (); } public synchronized void lockwrite () 던지기 InterruptedException {WriteRequests ++; while (독자> 0 || 작가> 0) {대기 (); } writeRequests-; 작가 ++; } public synchronized void unlockWrite ()는 InterruptedException {Writers-; notifyall (); }}readwritelock 클래스에서 읽기 잠금 장치 및 Write Lock은 각각 잠금을 획득하고 해제하는 방법이 있습니다.
Read Lock의 구현은 Lockread ()에 있습니다. 스레드에 Writ
Write Lock의 구현은 lockwrite ()에 있습니다. 스레드가 쓰기 잠금을 얻으려면 먼저 Writ 스레드가 읽기 잠금 장치 (readers == 0)를 보유하지 않고 스레드가 쓰기 잠금 장치 (Writers == 0)를 보유하지 않으면 쓰기 잠금을 얻을 수 있습니다. 잠금 장치를 쓰는 스레드 수는 중요하지 않습니다.
잠금 해제, UnlockWrite 모두에서 Notifyall 메소드가 알림 대신 호출된다는 점에 유의해야합니다. 이러한 이유를 설명하기 위해 다음과 같은 상황을 상상할 수 있습니다.
스레드가 읽기 잠금을 얻기 위해 대기 중이고 스레드가 쓰기 잠금을 획득하기 위해 기다리고있는 경우. 읽기 잠금을 기다리는 스레드 중 하나가 Notify 메소드에 의해 깨어나지 만 여전히 Writ 그러나, 쓰기 잠금을 기다리는 스레드 중 어느 것도 마치 아무 일도 일어나지 않은 것처럼 깨어났습니다 (번역기 주 : 신호 손실). NotifyAll 메소드를 사용하는 경우 모든 스레드가 깨어 난 다음 요청한 잠금을 얻을 수 있는지 여부를 결정합니다.
NotifyAll을 사용하면 하나의 이점이 있습니다. 다중 읽기 스레드가 읽기 잠금을 기다리고 있고 스레드가 쓰기 잠금을 기다리고 있지 않은 경우, UnlockWrite ()을 호출 한 후 읽기 잠금을 기다리는 모든 스레드는 한 번에 하나만 대신 읽기 잠금을 즉시 얻을 수 있습니다.
읽기/쓰기 잠금의 재입국
위에서 구현 된 읽기/쓰기 잠금 장치는 재진입이 아니며 이미 쓰기 잠금을 고정하는 스레드가 쓰기 잠금을 다시 요청할 때 차단됩니다. 그 이유는 이미 쓰기 실이 있기 때문입니다. 그 자체입니다. 또한 다음 예를 고려하십시오.
Readwritelock을 다시 설정하기 위해서는 일부 개선이 필요합니다. 다음은 Read Lock의 재진입과 Write Lock의 재진입을 각각 처리합니다.
잠금 리엔터를 읽으십시오
Readwritelock의 재진입의 읽기 잠금을 만들려면 먼저 읽기 잠금 재진입에 대한 규칙을 설정해야합니다.
스레드의 읽기 잠금 장치가 다시 들어가도록하려면 읽기 잠금 장치를 얻기위한 조건을 충족하거나 (쓰기 또는 쓰기 요청 없음), 쓰기 요청이 있는지 여부에 관계없이 이미 읽기 잠금을 누른 상태로 유지하십시오. 스레드가 이미 읽기 잠금 장치를 보유했는지 여부를 결정하기 위해 맵을 사용하여 이미 읽기 잠금을 보유한 스레드와 해당 스레드가 읽기 잠금을 얻는 횟수를 저장할 수 있습니다. 스레드가 읽기 잠금을 얻을 수 있는지 여부를 결정 해야하는 경우 맵에 저장된 데이터는 판단에 사용됩니다. 다음은 Lockread 및 잠금 해제 방법의 수정 된 코드입니다.
공개 클래스 readWritElock {private map <스레드, integer> readingthreads = new Hashmap <스레드, integer> (); 개인 int Writers = 0; 개인 int writeRequests = 0; public synchronized void lockread () 던지기 인터럽트 exception {스레드 callingthread = thread.currentthread (); while (! cangrantreadAccess (callingthread)) {wait (); } readingthreads.put (Callingthread, (getAccessCount (CallingThread) + 1)); } public synchronized void unlockread () {스레드 callingthread = thread.currentthread (); int accessCount = getAccessCount (CallingThread); if (accessCount == 1) {ReadingTheRreads.Remove (CallingThread); } else {readingthreads.put (Callingthread, (AccessCount -1)); } notifyall (); } private boolean cangrantreadAccess (스레드 callingthread) {if (writers> 0) false를 반환합니다. if (iSreader (CallingThread) Return true; if (writeRequests> 0)가 false를 반환합니다; return false; return true;} private int getReadAccessCount (resid CallingThread) {integer accesscount = readingThreads.get (CallingThread); if (accessCount == null) return 0; return.intValue (} private bolean isreader isreader isreader isreader isreader isreader isreader isreader isreader isreader. readingthreads.get (Callingthread)! = null}}코드에서, 우리는 스레드에 쓰기 잠금이없는 경우에만 읽기 잠금의 재입국이 허용되는 것을 알 수 있습니다. 또한 재진입 읽기 잠금 장치는 쓰기 잠금보다 우선 순위가 높습니다.
잠금 리엔터를 작성하십시오
스레드가 이미 쓰기 잠금 장치 (쓰기 잠금을 회복) 인 경우에만 Write Lock Reentry가 허용됩니다. 다음은 메소드 Lockwrite 및 UnlockWrite의 수정 된 코드입니다.
공개 클래스 readWritElock {private map <스레드, integer> readingthreads = new Hashmap <스레드, integer> (); 개인 int writeAccesses = 0; 개인 int writeRequests = 0; 개인 스레드 쓰기 thread = null; public synchronized void lockwrite () 던지기 InterruptedException {WriteRequests ++; Thread CallingThread = thread.currentThread (); while (! cangrantWriteAccess (callingthread)) {대기 (); } writeRequests-; WriteAccesses ++; WritingThread = Callingthread; } public synchronized void unlockWrite ()는 InterruptedException {writeAccesses-; if (writeAccesses == 0) {writingthread = null; } notifyall (); } private boolean cangrantWriteAccess (스레드 callingthread) {if (hasreaders ()) return false; if (writingthread == null) true를 반환합니다. if (! iswriter (callingthread)) false를 반환합니다. 진실을 반환하십시오. } private boolean hasreaders () {return ReadingThreads.size ()> 0; } private boolean iswriter (Thread Callingthread) {return writingthread == CallingThread; }}현재 스레드가 쓰기 잠금을 얻을 수 있는지 여부를 결정할 때 처리하는 방법에주의하십시오.
Write Lock의 잠금 업그레이드를 읽으십시오
때로는 쓰기 잠금을 얻기 위해 읽기 잠금 장치가있는 스레드를 원합니다. 이러한 작업을 허용하기 위해이 스레드는 읽기 잠금 장치가있는 유일한 스레드 여야합니다. Writelock () 은이 목표를 달성하기 위해 약간의 변경을해야합니다.
공개 클래스 readWritElock {private map <스레드, integer> readingthreads = new Hashmap <스레드, integer> (); 개인 int writeAccesses = 0; 개인 int writeRequests = 0; 개인 스레드 쓰기 thread = null; public synchronized void lockwrite () 던지기 InterruptedException {WriteRequests ++; Thread CallingThread = thread.currentThread (); while (! cangrantWriteAccess (callingthread)) {대기 (); } writeRequests-; WriteAccesses ++; WritingThread = Callingthread; } public synchronized void unlockWrite ()는 InterruptedException {writeAccesses-; if (writeAccesses == 0) {writingthread = null; } notifyall (); } private boolean cangrantwriteAccess (Thread Callingthread) {if (isonlyReader (CallingThread)) return true; if (hasreaders ())가 false를 반환합니다. if (writingthread == null) true를 반환합니다. if (! iswriter (callingthread)) false를 반환합니다. 진실을 반환하십시오. } private boolean hasreaders () {return ReadingThreads.size ()> 0; } private boolean iswriter (Thread Callingthread) {return writingthread == CallingThread; } private boolean isonlyReader (스레드 스레드) {return readers == 1 && readingTheRreads.get (Callingthread)! = null; }}이제 readwritelock 클래스를 읽기 잠금에서 쓰기 잠금으로 업그레이드 할 수 있습니다.
잠금을 읽으려면 잠금을 기록하십시오
때로는 쓰기 잠금 장치가있는 스레드도 읽기 잠금 장치를 얻기를 원합니다. 스레드에 쓰기 잠금이있는 경우 자연스럽게 다른 스레드에는 읽기 잠금 또는 쓰기 잠금을 가질 수 없습니다. 따라서 쓰기 잠금 장치가있는 스레드에 대한 위험은없고 읽기 잠금을 얻습니다. 위의 CangranTreadAccess 방법을 간단한 수정하면됩니다.
공개 클래스 readWritelock {private boolean cangrantreadAccess (스레드 CallingTheRdread) {if (iswriter (callingthread)) return true; if (writingthread! = null) false를 반환합니다. if (iSreader (Callingthread) return true; if (writeRequests> 0) false를 반환하고 true;}}}Reintrant ReadWritelock의 완전한 구현
아래는 완전한 ReadWritelock 구현입니다. 코드의 읽기 및 이해를 용이하게하기 위해 위의 코드는 단순히 재현되었습니다. 리팩토링 된 코드는 다음과 같습니다.
공개 클래스 readWritElock {private map <스레드, integer> readingthreads = new Hashmap <스레드, integer> (); 개인 int writeAccesses = 0; 개인 int writeRequests = 0; 개인 스레드 쓰기 thread = null; public synchronized void lockread () 던지기 인터럽트 exception {스레드 callingthread = thread.currentthread (); while (! cangrantreadAccess (callingthread)) {wait (); } readingthreads.put (Callingthread, (getReadAccessCount (CallingThread) + 1)); } private boolean cangrantreadAccess (스레드 callingthread) {if (iswriter (callingthread)) true; if (haswriter ())가 false를 반환합니다. if (iSreader (Callingthread)) true를 반환합니다. if (haswritequests ())가 false를 반환합니다. 진실을 반환하십시오. } public synchronized void unlockread () {스레드 callingthread = thread.currentthread (); if (! isReader (CallingTheRread)) {Throw New New ElegalMonitorStateException ( "호출 스레드는" + "가이 readWritElock에서 읽기 잠금을 유지하지 않음); } int accessCount = getReadAccessCount (CallingThread); if (accessCount == 1) {ReadingTheRreads.Remove (CallingThread); } else {readingthreads.put (Callingthread, (AccessCount -1)); } notifyall (); } public synchronized void lockwrite () 던지기 InterruptedException {WriteRequests ++; Thread CallingThread = thread.currentThread (); while (! cangrantWriteAccess (callingthread)) {대기 (); } writeRequests-; WriteAccesses ++; WritingThread = Callingthread; } public synchronized void unlockWrite ()는 InterpruptedException을 던지 셨습니다 {if (! iswriter (thread.currentThread ()) {throw new new IrongalMonitorStateException ( "호출 스레드"는 " +"이 readWritelock에서 쓰기 잠금을 고정하지 않습니다 "); 개인 부울 angrantwriteAccess (reside) if (ac readingthreads.get (Callingthread)! = null; } private boolean haswriter () {return writingthread! = null; } private boolean iswriter (Thread Callingthread) {return writingthread == CallingThread; } private boolean haswritequests () {return this.writeRequests> 0; }}마지막으로 Unlock ()을 호출하십시오
readWritelock을 사용하여 임계 영역을 보호 할 때, 임계 영역이 예외를 던질 수있는 경우, 마지막 블록에서 readUnlock () 및 writeUnlock ()을 호출하는 것이 중요합니다. 이는 readwritelock을 성공적으로 잠금 해제 할 수 있고 다른 스레드가 잠금을 요청할 수 있도록하기 위해 수행됩니다. 예는 다음과 같습니다.
lock.lockwrite (); try {// 예외를 던질 수있는 중요한 섹션 코드를 수행합니다} 마지막으로 {lock.unlockwrite ();}위의 코드 구조는 임계 영역에서 예외가 발생하면 readwritelock이 출시 될 수 있습니다. 마침내 블록에서 UnlockWrite 메소드가 호출되지 않으면 중요 섹션에서 예외가 발생하면 readwritelock은 쓰기 잠금 상태에 남아 있으므로 모든 스레드가 lockread () 또는 lekwrite ()을 블록으로 연결합니다. readwritelock을 재확인 할 수있는 유일한 요소는 readwritelock이 재진입된다는 것입니다. 예외가 발생하면 스레드는 잠금을 성공적으로 얻은 다음 중요 섹션을 실행하고 UnlockWrite ()를 다시 호출하여 readWritelock을 다시 출시 할 수 있습니다. 그러나 스레드가 더 이상 잠금을 얻지 못하면 어떻게됩니까? 따라서 UnlockWrite를 호출하는 것은 강력한 코드를 작성하는 데 매우 중요합니다.
위는 Java 다중 스레드 정보의 편집입니다. 우리는 향후 관련 정보를 계속 추가 할 것입니다. 이 웹 사이트를 지원 해주셔서 감사합니다!