어떤 면접관들은 학생들에게 자물쇠의 원리를 설명한 후 새 자물쇠를 다시 작성하라고 하고, 화이트보드에 전반적인 아이디어와 코드 논리를 적어달라고 요청하는 것을 좋아합니다. 개인적으로 두 부분에 중점을 두고 있다고 생각합니다.
잠금 원리에 대한 이해가 어떻게 시작되었는지 살펴보세요. 소스 코드를 읽지 않았다면 온라인 기사를 읽어보거나 뒷면에 있는 인터뷰 질문을 통해서도 알 수 있습니다. 일반적인 원칙이지만 실제로 소스 코드를 본 적이 없거나 잠금 관련 프로젝트에 대한 경험이 없으면 그 자리에서 잠금 구현 코드를 작성하기가 어렵습니다.
생성할 필요는 없습니다. Java 잠금의 기존 API를 모방하기 위해 다시 작성하면 됩니다.
소스 코드를 읽어보셨다면 이 질문은 정말 간단합니다. 익숙한 자물쇠를 선택하여 따라하시면 됩니다.
일반적으로 잠금을 사용자 정의할 때 요구 사항에 따라 정의합니다. 공유 잠금의 경우 공유 리소스와 같은 다양한 시나리오를 생각할 수 있습니다. 데이터베이스 링크에 대한 공유 액세스와 같이 읽기 잠금을 공유할 수 있습니다. 예를 들어 소켓 서버의 링크 수를 공유할 수 있습니다. 잠금을 정의하기 위해 데이터베이스 링크에 대한 공유 액세스 시나리오를 선택합니다.
우리의 데이터베이스가 10개의 연결만 지원할 수 있는 독립형 mysql이라고 가정합니다. 데이터베이스 링크를 생성할 때 가장 원시적인 JDBC 방법을 사용합니다. 인터페이스를 사용합니다. JDBC는 링크 생성 프로세스를 캡슐화합니다. 이 인터페이스의 이름은 Create Link Interface입니다.
공유 액세스 데이터베이스 링크에 대한 전체 요구 사항은 다음과 같습니다. 결합된 모든 요청에 대한 mysql 링크 수는 10(포함)을 초과할 수 없습니다. 10을 초과하면 오류가 직접 보고됩니다.
이 맥락에서 우리는 다음 그림을 디자인했습니다.
이 디자인의 가장 중요한 부분은 잠금을 얻을 수 있는지 여부에 따라 mysql 링크를 얻을 수 있는지 여부를 결정한다는 것입니다. 그런 다음 링크를 얻을 수 있습니다. 그렇지 않으면 오류가 직접 보고됩니다.
그럼 구현된 코드를 살펴보겠습니다.
먼저 정의에는 두 가지 요소가 필요합니다.
잠금 정의: 외부에서 제공되는 동기화 잠금; 그리고 잠금 해제.
공유 잠금의 코드 구현은 다음과 같습니다.
// 共享不公平锁 public class ShareLock implements Serializable{ // 同步器 private final Sync sync; // 用于确保不能超过最大值 private final int maxCount; /** * 初始化时给同步器 sync 赋值 * count 代表可以获得共享锁的最大值 */ public ShareLock(int count) { this.sync = new Sync(count); maxCount = count; } /** * 获得锁 * @return true 表示成功获得锁,false 表示失败 */ public boolean lock(){ return sync.acquireByShared(1); } /** * 释放锁 * @return true 表示成功释放锁,false 表示失败 */ public boolean unLock(){ return sync.releaseShared(1); } }
위 코드에서 볼 수 있듯이 잠금 및 해제 잠금 구현은 동기화 장치 Sync의 기본 구현에 의존합니다.
유일하게 주목해야 할 점은 잠금이 주로 두 가지 측면에서 API 사양을 지정해야 한다는 것입니다.
API에 필요한 것은 잠금이 초기화될 때 나에게 전달해야 하는 매개변수입니다. 초기화되면 최대 공유 가능한 잠금 수를 전달해야 합니다.
자체 기능을 정의해야 합니다. 즉, 각 메서드의 입력 매개 변수와 출력 매개 변수를 정의해야 합니다. ShareLock 구현에는 잠금 및 해제를 위한 입력 매개변수가 없습니다. 이는 메서드에 1로 하드코딩되어 있으며, 이는 메서드가 실행될 때마다 잠금이 한 번만 잠기거나 해제될 수 있음을 의미합니다. 매개변수는 부울 값이고 true는 추가를 의미합니다. 잠금 또는 잠금 해제가 성공했음을 의미하고, false는 실패를 의미하며, 맨 아래 계층에서는 동기화 불공정 잠금을 사용합니다.
위의 사고 방식에는 방법론이 있습니다. 즉, 문제에 대해 생각할 때 두 가지 측면에서 시작할 수 있습니다. API란 무엇입니까? API에는 어떤 기능이 있나요?
Sync는 AQS를 직접 상속합니다. 코드는 다음과 같습니다.
class Sync extends AbstractQueuedSynchronizer { // 表示最多有 count 个共享锁可以获得 public Sync(int count) { setState(count); } // 获得 i 个锁 public boolean acquireByShared(int i) { // 自旋保证 CAS 一定可以成功 for(;;){ if(i<=0){ return false; } int state = getState(); // 如果没有锁可以获得,直接返回 false if(state <=0 ){ return false; } int expectState = state - i; // 如果要得到的锁不够了,直接返回 false if(expectState < 0 ){ return false; } // CAS 尝试得到锁,CAS 成功获得锁,失败继续 for 循环 if(compareAndSetState(state,expectState)){ return true; } } } // 释放 i 个锁 @Override protected boolean tryReleaseShared(int arg) { for(;;){ if(arg<=0){ return false; } int state = getState(); int expectState = state + arg; // 超过了 int 的最大值,或者 expectState 超过了我们的最大预期 if(expectState < 0 || expectState > maxCount){ log.error("state 超过预期,当前 state is {},计算出的 state is {}",state ,expectState); return false; } if(compareAndSetState(state, expectState)){ return true; } } } }
전체 코드에서 주의해야 할 점은 다음과 같습니다.
경계에 대한 판단. 입력 매개변수가 불법인지, 잠금이 해제되면 발생하지 않을까요? 불법 상태 등의 경계 문제가 예상됩니다. 이러한 문제에 대한 판단을 내리고 사고의 엄격함을 반영해야 합니다.
잠금을 해제하려면; , 동시 추가가 발생할 때 잠금을 잠그거나 해제할 때 성공적으로 재시도할 수 있도록 spin + CAS를 사용해야 합니다. Spin용으로 작성 시에는 무한 루프가 발생하지 않도록 주의가 필요하며, CAS 방식은 AQS에서 제공하는 것이므로 직접 작성하지 마십시오.
잠금이 정의된 후에는 MysqlConnection이라는 Mysql 링크 도구 클래스를 작성했습니다. 주로 두 가지 큰 기능을 담당합니다:
JDBC를 통해 Mysql과의 링크 설정
요청이 너무 클 때 총 Mysql 링크 수가 10을 초과하지 않도록 잠금 기능과 결합.
먼저 MysqlConnection 초기화 코드를 살펴보겠습니다.
public class MysqlConnection { private final ShareLock lock; // maxConnectionSize 表示最大链接数 public MysqlConnection(int maxConnectionSize) { lock = new ShareLock(maxConnectionSize); } }
초기화 중에 최대 링크 수를 지정한 다음 이 값을 잠금에 전달해야 한다는 것을 알 수 있습니다. ShareLock 잠금 값의 상태입니다.
그런 다음 1을 완료하기 위해 비공개 메소드를 작성했습니다:
// 得到一个 mysql 链接,底层实现省略 private Connection getConnection(){}
그런 다음 2를 구현했으며 코드는 다음과 같습니다.
// 对外获取 mysql 链接的接口 // 这里不用try finally 的结构,获得锁实现底层不会有异常 // 即使出现未知异常,也无需释放锁 public Connection getLimitConnection() { if (lock.lock()) { return getConnection(); } return null; } // 对外释放 mysql 链接的接口 public boolean releaseLimitConnection() { return lock.unLock(); }
逻辑也比较简单,加锁时,如果获得了锁,就能返回 Mysql 的链接,释放锁时,在链接关闭成功之后,调用 releaseLimitConnection 方法即可,此方法会把锁的 state 状态加一,表示链接被释放了。
以上步骤,针对 Mysql 链接限制的场景锁就完成了。
锁写好了,接着我们来测试一下,我们写了一个测试的 demo,代码如下:
public static void main(String[] args) { log.info("模仿开始获得 mysql 链接"); MysqlConnection mysqlConnection = new MysqlConnection(10); log.info("初始化 Mysql 链接最大只能获取 10 个"); for(int i =0 ;i<12;i++){ if(null != mysqlConnection.getLimitConnection()){ log.info("获得第{}个数据库链接成功",i+1); }else { log.info("获得第{}个数据库链接失败:数据库连接池已满",i+1); } } log.info("模仿开始释放 mysql 链接"); for(int i =0 ;i<12;i++){ if(mysqlConnection.releaseLimitConnection()){ log.info("释放第{}个数据库链接成功",i+1); }else { log.info("释放第{}个数据库链接失败",i+1); } } log.info("模仿结束"); }
以上代码逻辑如下:
获得 Mysql 链接逻辑:for 循环获取链接,1~10 都可以获得链接,11~12 获取不到链接,因为链接被用完了;释放锁逻辑:for 循环释放链接,1~10 都可以释放成功,11~12 释放失败。
我们看下运行结果,如下图:
从运行的结果,可以看出,我们实现的 ShareLock 锁已经完成了 Mysql 链接共享的场景了。
위 내용은 Java rewrite lock의 설계 구조와 세부 사항은 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!