Maison  >  Article  >  Java  >  Explication détaillée des verrous de réentrance et des verrous en lecture-écriture dans la concurrence Java

Explication détaillée des verrous de réentrance et des verrous en lecture-écriture dans la concurrence Java

黄舟
黄舟original
2017-09-26 09:50:061603parcourir

Cet article présente principalement les verrous de réentrance et les verrous en lecture-écriture dans la programmation simultanée Java. Les exemples de codes pertinents dans l'article sont détaillés et disponibles pour les tests. Les amis qui en ont besoin peuvent en savoir plus.

Verrouillage de réentrée

Le verrouillage de réentrée, comme son nom l'indique, est un verrou qui prend en charge la réentrée, ce qui signifie que le verrou peut prendre en charge le verrouillage répété des ressources par un thread. La réentrée signifie que n'importe quel thread peut acquérir à nouveau le verrou sans être bloqué par le verrou après l'acquisition du verrou. L'implémentation de cette fonctionnalité doit résoudre les deux problèmes suivants.

1. Le fil acquiert à nouveau le verrou. Le verrou doit identifier si le thread qui acquiert le verrou est le thread qui occupe actuellement le verrou. Si tel est le cas, il peut être à nouveau acquis avec succès.

2. Le déverrouillage définitif du verrou. Le thread acquiert le verrou à plusieurs reprises n fois, puis après avoir libéré le verrou pour la nième fois, d'autres threads peuvent acquérir le verrou. La libération finale du verrou nécessite que le verrou incrémente le compte pour l'acquisition. Le compte représente le nombre de fois que le verrou actuel a été acquis à plusieurs reprises. Lorsque le verrou est libéré, le compte décrémente lorsque le compte est égal à 0, cela signifie que. le verrou a été libéré avec succès.

Le verrou intégré (synchroniser) et le verrou (ReentrantLock) en Java sont tous deux réentrants

instance synchronisée


package com.home;
public class SynchronizedTest implements Runnable {
  public synchronized void method1() {
    System.out.println("method1获得锁,正常运行!");
    method2();
  }
  public synchronized void method2() {
    System.out.println("method2获得锁,也正常运行!");
  }
  @Override
  public void run() {
    method1();
  }
  public static void main(String[] args) {
    SynchronizedTest st = new SynchronizedTest();
    new Thread(st).start();
    new Thread(st).start();
  }
}

Instance de verrouillage


package com.home;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockTest implements Runnable {
  Lock lock = new ReentrantLock();
  public void method1() {
    lock.lock();
    System.out.println("method1获得锁,正常运行!");
    method2();
    lock.unlock();
  }
  public void method2() {
    lock.lock();
    System.out.println("method2获得锁,也正常运行!");
    lock.unlock();
  }
  @Override
  public void run() {
    method1();
  }
  public static void main(String[] args) {
    LockTest lt = new LockTest();
    new Thread(lt).start();
    new Thread(lt).start();
  }
}

Les résultats finaux des deux exemples sont corrects comme suit :


method1获得锁,正常运行!
method2获得锁,也正常运行!
method1获得锁,正常运行!
method2获得锁,也正常运行!

Le rôle le plus important du verrouillage réentrant est d'éviter les blocages

Verrouillage en lecture-écriture

Le verrou en lecture-écriture maintient une paire de verrous associés, un pour les opérations en lecture seule et un pour les opérations en écriture. Tant qu'il n'y a pas d'écrivain, le verrou de lecture peut être détenu simultanément par plusieurs threads de lecture. Les verrous en écriture sont exclusifs.

ReentrantReadWriteLock

L'objet ReentrantReadWriteLock fournit les méthodes readLock() et writeLock() pour acquérir des verrous en lecture et en écriture.

Le verrou en lecture permet à plusieurs threads de lecture de le maintenir en même temps, tandis que le verrou en écriture ne peut être détenu que par un seul thread d'écriture au maximum.

Lire Cas d'utilisation des verrous en écriture : La fréquence de lecture des données partagées est bien supérieure à la fréquence de modification des données partagées. Dans les situations ci-dessus, l'utilisation de verrous en lecture-écriture pour contrôler l'accès aux ressources partagées peut améliorer les performances de concurrence.

Si un fil de discussion. qui détient déjà un verrou en écriture ne peut plus détenir un verrou en lecture-écriture. À l'inverse, si un thread détient déjà un verrou en lecture, il ne peut plus détenir un verrou en écriture jusqu'à ce que le verrou en lecture soit libéré.

Vous pouvez appeler la méthode newCondition() du verrou en écriture pour obtenir l'objet Condition lié au verrou en écriture. À l'heure actuelle, il n'y a aucune différence avec un verrou mutex ordinaire. Cependant, l'appel de la méthode newCondition du verrou en lecture (). lancera une exception.


exemple


package com.home;
import java.util.Random;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
class ReadWrte {
  // 共享数据,可以多个线程读数据,只能有一个线程写数据
  private int data;
  // 创建读写锁
  ReadWriteLock rwLock = new ReentrantReadWriteLock();
  /**
   * 读数据,上读锁
   */
  public void get() {
    // 读锁
    rwLock.readLock().lock();
    try {
      System.out.println(Thread.currentThread().getName() + ",Read!");
      Thread.sleep((long) Math.random() * 1000);
      System.out.println(Thread.currentThread().getName() + " 读出的数据为:" +
        this.getData());
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      rwLock.readLock().unlock();
    }
  }
  /**
   * 写数据,上写锁
   *
   * @param data
   */
  public void put(int data) {
    // 写锁
    rwLock.writeLock().lock();
    try {
      System.out.println(Thread.currentThread().getName() + ",Write!");
      Thread.sleep((long) Math.random() * 1000);
      this.setData(data);
      System.out.println(Thread.currentThread().getName() + " 写入的数据为:" +
        this.getData());
    } catch (InterruptedException e) {
      e.printStackTrace();
    } finally {
      rwLock.writeLock().unlock();
    }
  }
  public int getData() {
    return data;
  }
  public void setData(int data) {
    this.data = data;
  }
}
/**
 * 测试类
 *
 * @author itmyhome
 *
 */
public class ReadWriteLockTest {
  /**
   * @param args
   */
  public static void main(String[] args) {
    // 创建ReadWrte对象
    final ReadWrte rw = new ReadWrte();
    for (int i = 0; i < 10; i++) {
      // 创建并启动10个读线程
      new Thread(new Runnable() {
          @Override
          public void run() {
            rw.get();
          }
        }).start();
      // 创建并启动10个写线程
      new Thread(new Runnable() {
          @Override
          public void run() {
            // 写入一个随机数
            rw.put(new Random().nextInt(8));
          }
        }).start();
    }
  }
}
la sortie est


Thread-0,Read!
Thread-4,Read!
Thread-8,Read!
Thread-12,Read!
Thread-0 读出的数据为:0
Thread-4 读出的数据为:0
Thread-8 读出的数据为:0
Thread-12 读出的数据为:0
Thread-19,Write!
Thread-19 写入的数据为:5
Thread-7,Write!
Thread-7 写入的数据为:7
Thread-3,Write!
Thread-3 写入的数据为:4
Thread-16,Read!
Thread-16 读出的数据为:4
Thread-11,Write!
Thread-11 写入的数据为:0
Thread-15,Write!
Thread-15 写入的数据为:5
Thread-2,Read!
Thread-2 读出的数据为:5
Thread-17,Write!
Thread-17 写入的数据为:2
Thread-6,Read!
Thread-6 读出的数据为:2
Thread-1,Write!
Thread-1 写入的数据为:5
Thread-13,Write!
Thread-13 写入的数据为:4
Thread-9,Write!
Thread-9 写入的数据为:7
Thread-5,Write!
Thread-5 写入的数据为:2
Thread-10,Read!
Thread-10 读出的数据为:2
Thread-18,Read!
Thread-14,Read!
Thread-18 读出的数据为:2
Thread-14 读出的数据为:2
Sur l'image, nous pouvons voir que plusieurs threads peuvent lire en même temps, mais qu'un seul thread peut écrire, c'est-à-dire que l'écriture des données et l'écriture des données sont terminées en même temps.

Résumé

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!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn