Java가 단순히 동기화된 키워드를 사용하는 대신 잠금을 제공해야 하는 이유는 무엇입니까?
요약: 동기화 키워드는 하나의 스레드만 동기화된 코드 블록에 액세스할 수 있도록 Java에서 제공됩니다. syncised 키워드가 제공되는데 Java SDK 패키지에 Lock 인터페이스도 제공되는 이유는 무엇입니까? 이것이 불필요한 바퀴의 재발명인가? 오늘 우리는 이 문제에 대해 함께 논의해 보겠습니다.
synchronized
키워드는 하나의 스레드만 동기화된 코드 블록에 액세스할 수 있도록 Java에서 제공됩니다. synchronized
키워드가 제공되었는데 Java SDK 패키지에 Lock 인터페이스도 제공되는 이유는 무엇입니까? 이것이 불필요한 바퀴의 재발명인가요? 오늘 우리는 이 문제에 대해 함께 논의해 보겠습니다. synchronized
关键字来保证只有一个线程能够访问同步代码块。既然已经提供了synchronized
关键字,那为何在Java的SDK包中,还会提供Lock接口呢?这是不是重复造轮子,多此一举呢?今天,我们就一起来探讨下这个问题。
问题?
既然JVM中提供了synchronized关键字来保证只有一个线程能够访问同步代码块,为何还要提供Lock接口呢?这是在重复造轮子吗?Java的设计者们为何要这样做呢?让我们一起带着疑问往下看。
一、为何提供Lock接口?
很多小伙伴可能会听说过,在Java 1.5版本中,synchronized的性能不如Lock,但在Java 1.6版本之后,synchronized
做了很多优化,性能提升了不少。那既然synchronized关键字的性能已经提升了,那为何还要使用Lock呢?
如果我们向更深层次思考的话,就不难想到了:我们使用synchronized
加锁是无法主动释放锁的,这就会涉及到死锁的问题。
二、死锁问题
如果要发生死锁,则必须存在以下四个必要条件,四者缺一不可。
互斥条件
在一段时间内某资源仅为一个线程所占有。此时若有其他线程请求该资源,则请求线程只能等待。
不可剥夺条件
线程所获得的资源在未使用完毕之前,不能被其他线程强行夺走,即只能由获得该资源的线程自己来释放(只能是主动释放)。
请求与保持条件
线程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他线程占有,此时请求线程被阻塞,但对自己已获得的资源保持不放。
循环等待条件
在发生死锁时必然存在一个进程等待队列{P1,P2,…,Pn},其中P1等待P2占有的资源,P2等待P3占有的资源,…,Pn等待P1占有的资源,形成一个进程等待环路,环路中每一个进程所占有的资源同时被另一个申请,也就是前一个进程占有后一个进程所深情地资源。
三、synchronized的局限性
如果我们的程序使用synchronized
关键字发生了死锁时,synchronized关键是是无法破坏“不可剥夺”这个死锁的条件的。这是因为synchronized申请资源的时候, 如果申请不到, 线程直接进入阻塞状态了, 而线程进入阻塞状态, 啥都干不了, 也释放不了线程已经占有的资源。
然而,在大部分场景下,我们都是希望“不可剥夺”这个条件能够被破坏。也就是说对于“不可剥夺”这个条件,占用部分资源的线程进一步申请其他资源时, 如果申请不到, 可以主动释放它占有的资源, 这样不可剥夺这个条件就破坏掉了。
如果我们自己重新设计锁来解决synchronized
的问题,我们该如何设计呢?
四、解决问题
了解了synchronized的局限性之后,如果是让我们自己实现一把同步锁,我们该如何设计呢?也就是说,我们在设计锁的时候,要如何解决synchronized的局限性问题呢?这里,我觉得可以从三个方面来思考这个问题。
(1)能够响应中断。 synchronized
1. Lock 인터페이스를 제공하는 이유는 무엇인가요?
🎜많은 친구들이 Java 1.5 버전에서는 동기화 성능이 Lock만큼 좋지 않았지만 Java 1.6 버전 이후에는synchronized
가 많은 최적화를 거쳐 성능이 향상되었다는 이야기를 들었을 것입니다. 많이. 그렇다면 동기화 키워드의 성능이 향상되었는데 왜 여전히 Lock을 사용합니까? 🎜🎜더 깊게 생각해 보면 생각하기 어렵지 않습니다. synchronized
를 사용하여 잠금을 설정하면 잠금을 적극적으로 해제할 수 없으므로 교착 상태 문제가 발생합니다. 🎜2. 교착상태 문제
🎜 교착상태가 발생하려면 다음 네 가지 필수 조건이 있어야 하며, 이 네 가지 중 필수 조건은 없습니다. 🎜🎜
- 🎜🎜상호 배제 조건🎜🎜
- 🎜🎜양립할 수 없는 조건🎜🎜
- 🎜🎜요청 및 보류 조건🎜🎜
- 🎜🎜Loop 대기 조건🎜🎜
3. 동기화의 제한
🎜우리 프로그램이synchronized
키워드를 사용하고 교착 상태가 발생하는 경우 동기화의 핵심은 "양할 수 없는" 조건을 파괴할 수 없다는 것입니다. 왜냐하면 동기화를 자원에 적용할 때 응용이 불가능하면 스레드는 바로 차단 상태로 들어가고, 차단 상태에 들어가면 아무 것도 할 수 없고, 이미 스레드가 점유하고 있는 자원을 해제할 수도 없기 때문이다. 🎜🎜그러나 대부분의 시나리오에서는 "양도할 수 없는" 조건이 파기될 수 있기를 바랍니다. 즉, "비박탈" 조건의 경우, 일부 자원을 점유한 스레드가 다른 자원에 추가로 적용할 때, 적용할 수 없으면 자신이 점유하고 있는 자원을 적극적으로 해제할 수 있으므로 "비박탈" 조건이 됩니다. 박탈'이 파괴됩니다. 🎜🎜동기화
문제를 해결하기 위해 자물쇠를 스스로 다시 디자인한다면 어떻게 디자인해야 할까요? 🎜4. 문제 해결
🎜동기화의 한계를 이해한 후, 동기화 잠금을 직접 구현할 수 있다면 어떻게 설계해야 할까요? 즉, 잠금을 설계할 때 동기화의 한계를 어떻게 해결합니까? 여기서 우리는 이 문제를 세 가지 측면에서 생각해 볼 수 있다고 생각합니다. 🎜🎜
synchronized
의 문제점은 잠금 A를 보유한 후 잠금 B를 획득하려는 시도가 실패하면 스레드가 차단 상태에 들어간다는 것입니다. 일단 교착 상태가 발생하면 차단된 스레드를 깨울 기회가 없습니다. 실. 그러나 차단된 스레드가 인터럽트 신호에 응답할 수 있다면, 즉 차단된 스레드에 인터럽트 신호를 보내면 이를 깨울 수 있으며, 그러면 한때 보유했던 잠금 A를 해제할 기회를 갖게 됩니다. 이는 양도할 수 없는 조건을 위반합니다. 🎜(2) 지원 시간 초과. 스레드가 일정 시간 내에 잠금을 획득하지 못하고 차단 상태로 들어가는 대신 오류를 반환하는 경우 스레드는 한때 보유했던 잠금을 해제할 기회도 갖게 됩니다. 이는 또한 양도할 수 없는 조건을 약화시킬 것입니다.
(3) 비차단 방식으로 잠금을 획득합니다. 잠금 획득 시도가 실패하고 차단 상태로 진입하지 않고 직접 반환되는 경우 스레드는 한때 보유했던 잠금을 해제할 기회도 갖게 됩니다. 이는 또한 양도할 수 없는 조건을 약화시킬 것입니다.
는 Lock 인터페이스에서 제공하는 세 가지 메소드인
에 반영됩니다.
// 支持中断的API void lockInterruptibly() throws InterruptedException; // 支持超时的API boolean tryLock(long time, TimeUnit unit) throws InterruptedException; // 支持非阻塞获取锁的API boolean tryLock();
lockInterruptously()
는 인터럽트를 지원합니다.
tryLock() 메소드
tryLock() 메소드에는 잠금 획득을 시도하는 데 사용되는 반환 값이 있습니다. 획득에 성공하면 true를 반환합니다. , 다른 스레드에서 잠금을 획득한 경우) false를 반환합니다. 즉, 이 메서드는 무슨 일이 있어도 즉시 반환된다는 의미입니다. 자물쇠를 얻을 수 없을 때 거기에서 기다리지 않을 것입니다.
tryLock(장시간, TimeUnit 단위) 메서드
tryLock
(장시간, TimeUnit 단위) 메서드는 tryLock() 메서드와 유사하지만 유일한 차이점은 이 메서드가 잠금을 획득하지 못한 경우 일정 시간 동안 대기합니다. 제한 시간 내에 잠금을 획득하지 못한 경우 false를 반환합니다. 잠금이 처음에 획득되었거나 대기 기간 동안 획득된 경우 true를 반환합니다. tryLock
(long time, TimeUnit unit)方法和tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false。如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。
也就是说,对于死锁问题,Lock能够破坏不可剥夺的条件,例如,我们下面的程序代码就破坏了死锁的不可剥夺的条件。
public class TansferAccount{ private Lock thisLock = new ReentrantLock(); private Lock targetLock = new ReentrantLock(); //账户的余额 private Integer balance; //转账操作 public void transfer(TansferAccount target, Integer transferMoney){ boolean isThisLock = thisLock.tryLock(); if(isThisLock){ try{ boolean isTargetLock = targetLock.tryLock(); if(isTargetLock){ try{ if(this.balance >= transferMoney){ this.balance -= transferMoney; target.balance += transferMoney; } }finally{ targetLock.unlock } } }finally{ thisLock.unlock(); } } } }
例外,Lock下面有一个ReentrantLock
,而ReentrantLock
支持公平锁和非公平锁。
在使用ReentrantLock的时候, ReentrantLock中有两个构造函数, 一个是无参构造函数, 一个是传入fair参数的构造函数。 fair参数代表的是锁的公平策略, 如果传入true就表示需要构造一个公平锁, 反之则表示要构造一个非公平锁。如下代码片段所示。
//无参构造函数: 默认非公平锁 public ReentrantLock() { sync = new NonfairSync(); } //根据公平策略参数创建锁 public ReentrantLock(boolean fair){ sync = fair ? new FairSync() : new NonfairSync(); }
锁的实现在本质上都对应着一个入口等待队列, 如果一个线程没有获得锁, 就会进入等待队列, 当有线程释放锁的时候, 就需要从等待队列中唤醒一个等待的线程。 如果是公平锁, 唤醒的策略就是谁等待的时间长, 就唤醒谁, 很公平; 如果是非公平锁, 则不提供这个公平保证, 有可能等待时间短的线程反而先被唤醒。 而Lock是支持公平锁的,synchronized不支持公平锁。
最后,值得注意的是,在使用Lock加锁时,一定要在finally{}
try{ lock.lock(); }finally{ lock.unlock(); }로그인 후 복사예외입니다. 잠금 아래에
rrreee 잠금 구현은 기본적으로 항목 대기 대기열에 해당합니다. 스레드가 잠금을 획득하지 못한 경우 스레드가 잠금을 해제하면 대기 중인 스레드가 대기 대기열에서 깨어나야 합니다. 공정한 잠금인 경우 깨우기 전략은 오랫동안 기다려온 사람을 깨우는 것입니다. 이는 매우 공평하며, 불공정한 잠금인 경우에는 이러한 공정성이 보장되지 않으며 대기 시간이 짧은 스레드입니다. 먼저 깨어날 수도 있습니다. 잠금은 공정한 잠금을 지원하지만 동기화는 공정한 잠금을 지원하지 않습니다. 🎜🎜마지막으로 Lock을 사용하여 잠글 때는 예를 들어 다음 코드 조각에 표시된 것처럼ReentrantLock
이 있고ReentrantLock
은 공정한 잠금과 불공정한 잠금을 지원합니다. ReentrantLock을 사용할 때 ReentrantLock에는 두 개의 생성자가 있는데, 하나는 매개변수 없는 생성자이고 다른 하나는 공정한 매개변수를 전달하는 생성자입니다. fair 매개변수는 잠금의 공정성 전략을 나타냅니다. true가 전달되면 공정한 잠금이 생성되어야 함을 의미하고, 그렇지 않으면 불공정 잠금이 생성되어야 함을 의미합니다. 이는 다음 코드 조각에 나와 있습니다.finally{}
코드 블록에서 잠금을 해제해야 한다는 점에 유의할 가치가 있습니다. 🎜rrreee🎜🎜🎜참고: 🎜동기화 및 잠금에 대한 자세한 지침은 친구들이 직접 확인할 수 있습니다. 🎜🎜위 내용은 Java가 단순히 동기화된 키워드를 사용하는 대신 잠금을 제공해야 하는 이유는 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











자바의 암스트롱 번호 안내 여기에서는 일부 코드와 함께 Java의 Armstrong 번호에 대한 소개를 논의합니다.

Java의 난수 생성기 안내. 여기서는 예제를 통해 Java의 함수와 예제를 통해 두 가지 다른 생성기에 대해 설명합니다.

Java의 Weka 가이드. 여기에서는 소개, weka java 사용 방법, 플랫폼 유형 및 장점을 예제와 함께 설명합니다.

Java의 Smith Number 가이드. 여기서는 정의, Java에서 스미스 번호를 확인하는 방법에 대해 논의합니다. 코드 구현의 예.

이 기사에서는 가장 많이 묻는 Java Spring 면접 질문과 자세한 답변을 보관했습니다. 그래야 면접에 합격할 수 있습니다.

Java 8은 스트림 API를 소개하여 데이터 컬렉션을 처리하는 강력하고 표현적인 방법을 제공합니다. 그러나 스트림을 사용할 때 일반적인 질문은 다음과 같은 것입니다. 기존 루프는 조기 중단 또는 반환을 허용하지만 스트림의 Foreach 메소드는이 방법을 직접 지원하지 않습니다. 이 기사는 이유를 설명하고 스트림 처리 시스템에서 조기 종료를 구현하기위한 대체 방법을 탐색합니다. 추가 읽기 : Java Stream API 개선 스트림 foreach를 이해하십시오 Foreach 메소드는 스트림의 각 요소에서 하나의 작업을 수행하는 터미널 작동입니다. 디자인 의도입니다
