자바의 자물쇠는 무엇입니까?
<Java Concurrent Programming>을 읽은 후에이 질문에 대답 할 수 없었습니다. 그래서 나는 책의 내용을 다시 살펴 보았고 갑자기 이마를 열고있는 것처럼 느꼈습니다. 배우는 가장 좋은 방법은 문제로 배우고 해결하는 것입니다.
Java에는 두 가지 주요 잠금 유형의 잠금 유형이 있습니다 : 내부 잠금 동기화 및 디스플레이 잠금 잠금 장치 java.util.concurrent.locks.lock. 그러나 신중하게 생각하면 요약이 옳지 않은 것 같습니다. Java 내장 잠금 장치와 동시에 구현 된 일련의 잠금 장치 여야합니다.
Java에서 모든 것이 물체이고 Java는 각 객체에 잠금 장치가 내장되어있어 객체 잠금/내부 잠금이라고도 할 수 있기 때문에 왜 이것이 말하는가? 관련 잠금 작동은 동기화 된 것을 통해 완료됩니다.
동기화 된 구현의 결함과 동시 시나리오의 복잡성으로 인해 누군가가 명시 적 잠금 장치를 개발했으며 이러한 잠금은 java.util.concurrent.locks.locks에서 파생됩니다. 물론 JDK1.5 이상 버전에 내장되었습니다.
동기화
먼저, 더 자주 사용되는 동기화 된 것을 살펴 보겠습니다. 그것은 또한 나의 일상 업무에도 사용됩니다. 동기화 된 것은 특정 코드 블록에 대한 잠금 메커니즘을 제공하는 데 사용됩니다. 암시 적으로 Java 개체를 잠금 할 것입니다. 이 잠금 장치를 고유 또는 모니터 잠금이라고합니다. 스레드는 코드가 완료된 후 자동으로 릴리스 될 때까지 동기화 된 블록을 입력하기 전에이 잠금 장치를 자동으로 획득합니다 (또는 예외 일 수도 있음). 내장 잠금 장치는 상호 배타적입니다. 잠금은 동시에 하나의 스레드로만 고정 될 수 있으며, 이는 여러 스레드로 이어질 수 있으며 잠금의 실이 잡힌 후에 차단됩니다. 이를 통해 코드의 스레드 안전은 원자력을 보장 할 수 있습니다.
재 입력
Java 내장 잠금 장치는 상호 배타적이며 후속 스레드가 막히게되므로 잠금을 얻으려고 할 때 잠금을 고정하는 스레드가 다시 들어 오면 어떻게됩니까? 예를 들어 다음 상황 중 하나입니다.
public class baseclass {public synchronized void do () {system.out.println ( "is base"); }} public class sonclass는 baseclass {public synchronized void do () {system.out.println ( "is son"); super.do (); }} sonclass son = new sonclass (); son.do ();이 시점에서 파생 클래스의 DO 메소드는 먼저 잠금 장치를 한 번 고정 한 다음 Super.do ()를 호출 할 때 잠금을 다시 입력하고 다시 잡습니다. 자물쇠가 상호 배타적이면 현재 교착 상태가되어야합니다.
그러나 내부 잠금 장치는 재진입의 특성, 즉 잠금 장치가 재진입 메커니즘, 참조 수 관리를 구현하기 때문에 결과는 그렇지 않습니다. 스레드 1이 객체의 잠금 A를 보유하면 잠금 A에 대한 참조는 1을 추가하여 계산됩니다. 그런 다음 스레드 1을 획득하면 스레드 1이 여전히 잠금을 유지하면 계산이 1을 추가합니다. 물론, 동기화 블록을 종료 할 때마다 0이 될 때까지 1이 줄어 듭니다.
동기화 된 일부 기능
코드를 수정하는 방법
수정 방법
public class baseclass {public synchronized void do () {system.out.println ( "is base"); }}이것은 메소드를 직접 잠그는 것을 의미 하며이 메소드 블록을 입력 할 때 잠금을 얻어야합니다.
코드 블록을 수정하십시오
public class baseclass {private static 객체 잠금 = new Object (); public void do () {synchronized (lock) {system.out.println ( "is base"); }}}여기서 잠금의 범위는이 방법의 일부 코드 블록으로 축소되어 잠금의 유연성을 향상시킵니다. 결국, 잠금의 세분성 제어도 잠금의 주요 문제입니다.
객체 잠금 유형
종종 일부 코드는 특수 용어로 동기화 된 것을보고 다음 코드를 살펴 봅니다.
public class baseclass {private static 객체 잠금 = new Object (); public void do () {synchronized (lock) {}} public synchronized void dovoid () {} public synchronized static void dostaticVoid () {} public static void dostaticVoid () {synchronized (baseclass.class) {}}}여기에는 코드 블록 수정, 메소드 수정, 정적 메소드 수정 및베이스 클래스의 클래스 개체 수정의 네 가지 상황이 있습니다. 그렇다면 이러한 상황의 차이점은 무엇입니까?
코드 블록을 수정하십시오
이 경우 코드에서 동기화 된 (잠금)를 사용하여 객체 잠금을 만듭니다. 즉, 객체의 내장 잠금을 사용하는 것을 의미합니다. 이 경우 잠금 컨트롤이 물체로 넘겨집니다. 물론이 작업을 수행하는 또 다른 방법이 있습니다.
public void do () {synchronized (this) {system.out.println ( "is base"); }}이것을 사용한다는 것은 현재 객체의 잠금을 의미합니다. 내장 잠금 장치의 핵심도 여기에 언급되어 있습니다. 이 코드를 보호하기위한 잠금 장치를 제공합니다. 어떤 스레드가 오든 상관없이, 그것은 같은 잠금에 직면하게됩니다.
개체 수정 방법
이 직접 수정의 상황은 무엇입니까? 실제로, 이것은 기본적으로 현재 객체의 잠금이라는 점을 제외하고 코드 블록 수정과 유사합니다. 이런 식으로 코드를 작성하는 것은 비교적 간단하고 명확합니다. 앞에서 언급했듯이 코드 블록 수정의 차이는 주로 과립력 제어의 차이입니다.
정적 메소드를 수정하십시오
정적 방법에 대해 다른 것이 있습니까? 실제로 다릅니다. 이 시점에서 획득 한 자물쇠는 더 이상이 아니며,이 객체에 의해 지적 된 클래스는 클래스 잠금입니다. Java의 클래스 정보는 메소드 상수 영역에로드되므로 글로벌은 고유합니다. 이것은 실제로 글로벌 잠금 장치를 제공합니다.
수정 된 클래스의 클래스 객체
이 상황은 실제로 정적 방법을 수정할 때와 매우 유사하지만 여전히 같은 이유입니다. 이 방법은보다 유연한 제어 세분성을 제공 할 수 있습니다.
요약
이러한 상황에 대한 분석 및 이해를 통해 내장 잠금의 주요 핵심 개념은 상호 배타적으로 사용될 수있는 잠금 장치가있는 코드 조각을 제공하고 스위치와 유사한 기능을 수행하는 것임을 알 수 있습니다.
Java는 또한 내장 잠금 장치에 대한 일부 구현을 제공합니다. 주요 특징은 Java가 모든 객체이고 각 객체에는 잠금 장치가 있으므로 상황에 따라 사용할 잠금 장치를 선택할 수 있다는 것입니다.
java.util.concurrent.locks.lock
나는 이전에 동기화 된 것을 보았다. 대부분의 경우 거의 충분합니다. 그러나이 시스템은 동시 프로그래밍에서 점점 더 복잡해지기 때문에 동기화 된 처리가 더 어려운 시나리오가 항상 있습니다. 또는 <Java Concurrent Programming>에 언급 된 바와 같이, 동시 잠금은 내부 잠금 장치를 보완하여보다 고급 기능을 제공합니다.
java.util.concurrent.locks.lock의 간단한 분석
이 인터페이스는 잠금의 주요 작동을 추상화하므로 잠금에서 파생 된 잠금 장치가 무조건적이고 순환 가능하며시기 가능하며 중단 가능합니다. 또한 잠금 및 잠금 해제 작업은 명시 적으로 수행됩니다. 코드는 다음과 같습니다.
공개 인터페이스 잠금 {void lock (); void lockinterruptibly ()가 중단 된 예를 던진다. 부울 trylock (); 부울 트리 록 (오랜 시간, 시간 유닛)은 중단 된 예를 던진다. 무효 잠금 해제 (); 조건 NewCondition ();} 재진입 락
ReintrantLock은 재진입 잠금 장치이며 이름조차도 명백합니다. ReintrantLock은 동기화와 유사한 의미론을 제공하지만 ReentrantLock은 다음과 같이 명시 적으로 호출되어야합니다.
공개 클래스베이스 클래스 {개인 잠금 잠금 = 새로운 재 렌트 런 락 (); public void do () {lock.lock (); try {// ..} 마침내 {lock.unlock (); }}}이 방법은 코드 읽기에 매우 명확하지만 문제가 있습니다. 즉, 마침내 시도를 추가하거나 잠금을 쓰는 것을 잊어 버린 경우 잠금 장치 ()를 쓸 수 있으면 잠금 장치가 해제되지 않아 일부 교착 상태로 이어질 수 있습니다. 동기화의 위험은 없습니다.
Trylock
ReintrantLock은 잠금 인터페이스를 구현하므로 자연스럽게 TryLock을 포함한 기능이 있습니다. TryLock은 자물쇠를 얻으려고합니다. 자물쇠가 다른 스레드에 의해 점유 된 경우 즉시 False를 반환합니다. 그렇지 않은 경우, 점유하고 True를 반환해야합니다. 즉, 자물쇠가 얻어 졌음을 의미합니다.
다른 TryLock 메소드에는 매개 변수가 포함되어 있습니다. 이 방법의 기능은 시간을 지정하는 것입니다. 즉,이 시간 동안 잠금을 계속 얻으려고 노력하고 시간이 얻어지지 않은 경우 포기합니다.
TryLock이 항상 자물쇠를 차단하고 기다리지는 않기 때문에 교착 상태가 더 많이 발생하지 않을 수 있습니다.
무질서하게
스레드가 잠금을 획득 할 때 인터럽트 적으로 인터럽트에 응답합니다. 인터럽트가 감지되면 상단 계층 코드에 의해 인터럽트 예외가 발생합니다. 이 경우 라운드 로빈 잠금 장치에 대한 출구 메커니즘이 제공됩니다. 인터럽트 잠금 작업을 더 잘 이해하기 위해 데모를 이해하기 위해 작성되었습니다.
패키지 com.test; import java.util.date; import java.util.concurrent.locks.reentrantlock; public class testlockinterruptibly {static reentrantlock lock = new ReentrantLock (); public static void main (string [] args) {스레드 스레드 1 = new Thread (new Runnable () {@override public void run () {try {doprint ( "스레드 1 get lock."); do123 (); doprint ( "스레드 1 끝."); Thread 2 = new Thread (new Runnable () {@override public void run () {try {doprint ( "스레드 2 get lock."); doprint ( "Thread 2 End.");} catch (InterruptedException e) {doprint ( "스레드 2가 중단됩니다");}}); thread1.setName ( "strook1"); Thread2.SetName ( "Thread2"); thread1.start (); try {strook.sleep (100); // Thread2} catch (InterruptedException e) {e.printstacktrace (); } thread2.start (); } private static void do123 ()는 interruptedException {lock.lockinterruptively (); doprint (thread.currentThread (). getName () + "잠금되었습니다."); {doprint (thread.currentthread (). getName () + "dosoming1 ...."); Thread.Sleep (5000); // 스레드의 순서를 촉진하기 위해 몇 초 동안 ware 다. doprint (thread.currentthread (). getName () + "완료되었습니다."); } 마침내 {lock.unlock (); }} private static void doprint (문자열 텍스트) {system.out.println ((new date ()). tolocalestring () + ":" + text); }}위 코드에는 두 개의 스레드가 있습니다. Thread1은 Thread2보다 일찍 시작됩니다. 잠금 프로세스를보기 위해 잠긴 코드는 5 초 동안 잠을 자고 잠금 획득 프로세스에 들어가는 첫 번째 및 두 번째 스레드의 프로세스를 느낄 수 있습니다. 위 코드의 최종 결과는 다음과 같습니다.
2016-9-28 15:12:56 : 스레드 1은 잠금을 얻습니다.
2016-9-28 15:12:56 : Thread1이 잠겨 있습니다.
2016-9-28 15:12:56 : Thread1 Dosoming1 ....
2016-9-28 15:12:56 : 스레드 2는 잠금을 얻습니다.
2016-9-28 15:13:01 : Thread1 Dosoming2 ....
2016-9-28 15:13:01 : Thread1이 완료되었습니다.
2016-9-28 15:13:01 : 스레드 1이 언로드되었습니다.
2016-9-28 15:13:01 : Thread2가 잠겨 있습니다.
2016-9-28 15:13:01 : Thread2 Dosoming1 ....
2016-9-28 15:13:01 : 스레드 1 끝.
2016-9-28 15:13:06 : Thread2 DoSoming2 ....
2016-9-28 15:13:06 : Thread2가 완료되었습니다.
2016-9-28 15:13:06 : 스레드 2가 언로드되었습니다.
2016-9-28 15:13:06 : 스레드 2 끝.
Thread1은 먼저 잠금 잠금 장치를 얻는다는 것을 알 수 있으며 Thread2는 나중에 잠금을 얻을 수 있지만 현재 Thread1은 잠금을 점유 했으므로 Thread2가 잠금을 방출 할 때까지 Lock을 얻지 못합니다.
**이 코드는 잠금 장치를 얻기 위해 잠금식 뒤의 스레드가 잠금을 얻기 전에 이전 잠금 장치가 해제 될 때까지 기다려야 함을 보여줍니다. ** 그러나 아직 인터럽 가능한 기능이 없으므로 일부 코드가 추가됩니다.
Thread2.start (); try {thread.sleep (1000); } catch (InterpruptedException e) {e.printstacktrace ();} // 1 초에서 stread2를 인터럽트합니다.Thread2가 시작된 후 Thread2의 인터럽트 방법을 호출하십시오. 자, 먼저 코드를 실행하고 결과를 확인하십시오.
2016-9-28 15:16:46 : 스레드 1은 잠금을 얻습니다.
2016-9-28 15:16:46 : Thread1이 잠겨 있습니다.
2016-9-28 15:16:46 : Thread1 Dosoming1 ....
2016-9-28 15:16:46 : 스레드 2는 잠금을 얻습니다.
2016-9-28 15:16:47 : 스레드 2가 중단되었습니다. <-스레드 인터럽트에 직접 응답합니다
2016-9-28 15:16:51 : Thread1 Dosoming2 ....
2016-9-28 15:16:51 : Thread1이 완료되었습니다.
2016-9-28 15:16:51 : 스레드 1이 언로드되었습니다.
2016-9-28 15:16:51 : 스레드 1 끝.
이전 코드와 비교하여 Thread2가 Lock을 해제하기를 기다리고 있지만 Thread2 자체가 인터럽트하고 Thread2 뒤의 코드는 계속 실행되지 않습니다.
readwritelock
이름에서 알 수 있듯이, 그것은 읽기 쓰기 잠금입니다. 이러한 종류의 읽기 쓰기 잠금 애플리케이션 시나리오는 이러한 방식으로 이해할 수 있습니다. 예를 들어, 데이터의 물결은 주로 읽기를 위해 제공되며 비교적 적은 수의 쓰기 작업 만 있습니다. Mutex 잠금 장치가 사용되면 스레드 간의 잠금 경쟁으로 이어집니다. 모든 사람이 읽을 때 읽을 수 있다면 자원이 작성되면 자원을 잠그십시오. 이러한 변경 사항은이 문제를 잘 해결하므로 읽기 작업이 쓰기 작업에 영향을 미치지 않고 읽기 성능을 향상시킬 수 있습니다.
리소스는 여러 독자가 액세스하거나 한 작가가 액세스 할 수 있으며 둘 다 동시에 수행 할 수 없습니다.
이것은 읽기 및 쓰기 잠금의 추상 인터페이스로, 읽기 잠금 및 쓰기 잠금을 정의합니다.
공개 인터페이스 readWritElock { /*** 읽기에 사용 된 잠금 장치를 반환합니다. * * @return 읽기에 사용되는 잠금 */ 잠금 readlock (); /*** 작성에 사용되는 잠금 장치를 반환합니다. * * @return 작성에 사용되는 잠금 */ 잠금 writelock ();}JDK에는 ReentrantreadWritelock 구현이 있으며, 이는 재입국 읽기 작성 잠금 장치입니다. ReentrantreadWritelock은 공정하거나 불공평 한 두 가지 유형으로 구성 될 수 있습니다. 시공 중에 명시 적으로 지정되지 않은 경우 기본적으로 비전제 잠금 장치가 생성됩니다. 비-대항 잠금 모드에서, 스레드 액세스 순서는 불확실합니다. 즉, 깨질 수 있습니다. 작가에서 독자로 다운 그레이드 될 수 있지만 독자는 작가로 업그레이드 할 수 없습니다.
공정한 잠금 모드 인 경우 옵션이 가장 긴 대기 시간으로 스레드에 넘겨집니다. 읽기 스레드가 잠금을 얻고 쓰기 스레드가 쓰기 잠금을 요청하면 쓰기 작업이 완료 될 때까지 읽기 잠금 획득은 더 이상 수신되지 않습니다.
간단한 코드 분석은 실제로 ReentrantreadWritelock에서 동기화 잠금을 유지하지만 의미 적으로 읽기 잠금 및 쓰기 잠금과 비슷합니다. 생성자를 살펴보십시오.
Public ReentrantreadWritelock (Boolean Fair) {Sync = Fair? New FairSync () : New NonFairsync (); ReaderLock = 새로운 재입학 (this); WriterLock = New Writelock (this);} // 읽기 잠금 보호 레디 록의 생성자 (ReentrantreadWritelock Lock) {sync = lock.sync;} // Write Lock Protected WriteLock (ReentranTreadWritelock Lock) {sync = lock.sync;}의 생성자.ReentrantreadWritelock의 동기 잠금 객체가 실제로 구성 될 때 참조되어 있음을 알 수 있습니다. 이 동기화 클래스는 ReentrantreadWritelock의 내부 클래스입니다. 요컨대, 읽기/쓰기 잠금은 모두 동기화를 통해 수행됩니다. 둘 사이의 관계에 대해 어떻게 협력합니까?
// 잠금 잠금 잠금 장치 공개 void lock () {sync.acquireshared (1);} // 잠금 잠금 장치 공개 void lock () {sync.acquire (1);}주요 차이점은 읽기 잠금이 공유 잠금을 얻는 반면, 쓰기 잠금은 독점 잠금을 얻는다는 것입니다. 여기에는 언급 할 수있는 요점, 즉 ReentrantreadWritelock을 보장하기 위해 공유 잠금 장치와 독점 잠금 장치는 보류 카운트 및 재 신석방을 지원해야합니다. ReintrantLock은 상태를 사용하여 저장되며 상태는 하나의 성형 값 만 저장할 수 있습니다. 두 잠금 장치의 문제와 호환 되려면 공유 잠금 장치를 보유하는 스레드 수 또는 독점 잠금 또는 재진입 카운트를 각각 보유하는 스레드 수로 나뉩니다.
다른
글을 쓰는 데 너무 오래 걸리는 것처럼 느껴지는 큰 기사를 썼으며 더 유용한 자물쇠가 있습니다.
CountdownLatch
동시에 고정 된 카운터를 설정하는 것입니다. 발신자가 CountdownLatch의 AWAIT 메소드를 호출하면 현재 카운터가 0이 아닌 경우 차단됩니다. CountdownLatch의 릴리스 방법을 호출하면 호출 대기업이 차단 해제 될 때까지 카운트를 줄일 수 있습니다.
세마 반
세마포어는 100 개의 라이센스 설정과 같은 권한 부여 및 라이센스의 한 형태로, 100 개의 스레드가 동시에 잠금을 고정 할 수 있으며이 금액을 초과하면 실패로 돌아갑니다.
이 기사를 읽어 주셔서 감사합니다. 도움이되기를 바랍니다. 이 사이트를 지원 해주셔서 감사합니다!