Certains intervieweurs aiment demander aux étudiants de réécrire une nouvelle serrure après avoir expliqué le principe de la serrure, et vous demandent d'écrire l'idée générale et la logique du code sur le tableau blanc. Ce genre d'entretien La question est assez. difficile. Personnellement, je pense qu'il se concentre sur deux parties :
Examinez d'où vient votre compréhension du principe de verrouillage. Si vous n'avez pas lu le code source, lisez simplement les articles en ligne, ou Les questions d'entretien au dos peuvent également expliquer le général. principe, mais il vous est difficile d'écrire un code d'implémentation de verrouillage sur place, à moins d'avoir vraiment vu le code source, ou d'avoir de l'expérience dans des projets liés au verrouillage
Nous n'avons pas besoin de créer, nous juste cela ; doit être réécrit pour imiter l'API existante dans le verrouillage Java.
Si vous avez lu le code source, cette question est très simple. Vous pouvez choisir une serrure que vous connaissez à imiter.
Généralement, lors de la personnalisation des verrous, nous les définissons en fonction des exigences. Il est impossible de définir des verrous à partir de rien. Lorsqu'il s'agit de verrous partagés, vous pouvez penser à de nombreux scénarios, comme pour les ressources partagées. Les verrous de lecture peuvent être partagés, comme l'accès partagé aux liens de base de données. Par exemple, le nombre de liens sur le serveur Socket peut être partagé. Il existe de nombreux scénarios. Nous choisissons le scénario d'accès partagé aux liens de base de données pour définir un verrou.
Supposons (les hypothèses suivantes sont toutes des hypothèses) que notre base de données est un MySQL autonome, qui ne peut prendre en charge que 10 connexions. Lors de la création d'un lien de base de données, nous utilisons la méthode JDBC la plus primitive, et nous. utiliser une interface. JDBC encapsule le processus de création d'un lien. Nous nommons cette interface : Create Link Interface.
Les exigences globales pour les liens de base de données à accès partagé sont les suivantes : le nombre de liens mysql pour toutes les requêtes combinées ne peut pas dépasser 10 (inclus). Une fois qu'il dépasse 10, une erreur sera signalée directement.
Dans ce contexte, nous avons conçu l'image suivante :
La partie la plus critique de cette conception est que nous décidons si nous pouvons obtenir le lien mysql en fonction de si nous pouvons obtenir le verrou. alors vous pouvez obtenir le lien, sinon une erreur sera signalée directement.
Jetons ensuite un œil au code implémenté :
Nous devons d'abord définir un verrou. La définition nécessite deux éléments :
Définition du verrou : synchroniseur de verrouillage fourni en externe. Méthodes de verrouillage. et déverrouillage.
L'implémentation du code du verrou partagé est la suivante :
// 共享不公平锁 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); } }
Comme le montre le code ci-dessus, l'implémentation du verrouillage et de la libération des verrous repose sur l'implémentation sous-jacente du synchroniseur Sync.
La seule chose à noter est que le verrou doit spécifier les spécifications de l'API, principalement sous deux aspects :
Ce que l'API exige, ce sont les paramètres que vous devez me transmettre lorsque le verrou est initialisé. est initialisé, vous devez passer le maximum Le nombre de verrous partageables
Doit définir ses propres capacités, c'est-à-dire définir les paramètres d'entrée et les paramètres de sortie de chaque méthode. Dans l'implémentation de ShareLock, il n'y a pas de paramètres d'entrée pour verrouiller et libérer les verrous. Ils sont codés en dur 1 dans la méthode, ce qui signifie que chaque fois que la méthode est exécutée, le verrou ne peut être verrouillé qu'une seule fois ou libéré une seule fois. Le paramètre est une valeur booléenne, et true signifie que l'ajout du verrouillage ou de la libération du verrou est réussi, false indique un échec et la couche inférieure utilise des verrous injustes de synchronisation.
La façon de penser ci-dessus a une méthodologie, c'est-à-dire que lorsque nous réfléchissons à un problème, nous pouvons partir de deux aspects : Qu'est-ce qu'une API ? Quelles sont les capacités de l'API ?
Sync hérite directement d'AQS. Le code est le suivant :
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; } } } }
L'ensemble du code est relativement clair. Ce à quoi nous devons prêter attention est :
Le jugement de la frontière, tel que. si le paramètre d'entrée est illégal, cela ne se produira-t-il pas lorsque le verrou sera libéré ? Il y aura des problèmes de limites tels qu'un état illégal. Nous devons juger de tels problèmes et refléter la rigueur de la réflexion ; vous devez utiliser le formulaire for spin + CAS pour vous assurer que lors de l'ajout simultané Lorsque vous verrouillez ou relâchez le verrou, vous pouvez réessayer avec succès. Lors de l'écriture pour spin, nous devons faire attention à revenir au moment approprié et à ne pas provoquer de boucle infinie. La méthode CAS a été fournie par AQS. Ne l'écrivez pas vous-même. La méthode CAS que nous écrivons nous-mêmes ne peut pas garantir l'atomicité.
2.3. La possibilité d'obtenir le lien est déterminée par la possibilité d'obtenir le verrou
Établir un lien avec Mysql via JDBC
Combiné avec un verrou pour empêcher le nombre total de liens Mysql de dépasser 10 lorsque la requête est trop volumineuse ;
Tout d'abord, jetons un œil au code d'initialisation de MysqlConnection :
public class MysqlConnection { private final ShareLock lock; // maxConnectionSize 表示最大链接数 public MysqlConnection(int maxConnectionSize) { lock = new ShareLock(maxConnectionSize); } }
Nous pouvons voir que lors de l'initialisation, nous devons spécifier le nombre maximum de liens, puis transmettre cette valeur au verrou, car le nombre maximum de liens est l'état de la valeur du verrou ShareLock.
Puis afin de compléter 1, nous avons écrit une méthode privée :
// 得到一个 mysql 链接,底层实现省略 private Connection getConnection(){}
Puis nous avons implémenté 2, le code est le suivant :
// 对外获取 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 链接共享的场景了。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!