Les getters / setters des types primitifs devraient-ils être verrouillés avec ReadWriteLock dans une application multithreading?

StackOverflow https://stackoverflow.com/questions/5014266

Question

J'ai une classe Java qui est utilisée dans une application multithreading. Un accès simultané est très probable. Plusieurs opérations de lecture simultanées ne doivent pas bloquer, donc j'utilise un verrou ReadWrite.

class Example {
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private int i;
    private boolean b;

    public int getI() {
      lock.readLock().lock();
      final int tmp = i;
      lock.readLock().unlock(),
      return tmp;
    }

    public void setI( final int i ) {
      lock.writeLock().lock();
      this.i = i;
      lock.writeLock().unlock();
    }

    public boolean getB() {
      lock.readLock().lock();
      final boolean tmp = b;
      lock.readLock().unlock(),
      return tmp;
    }

    public void setB( final boolean b ) {
      lock.writeLock().lock();
      this.b = b;
      lock.writeLock().unlock();
    }
}

Pour simplicité j'ai omis le try...finally bloque autour des serrures dans cet exemple.

Je me demande s'il est nécessaire (ou disons recommandés) de verrouiller / synchroniser les getters et les sets de types primitifs? Je sais que les opérations de l'attribution et de retour en Java sont atomiques. Cependant, en utilisant ces serrures, je ne m'assure pas que chaque accessoire obtient la dernière valeur (équivaut à l'utilisation volatile)?

Et si les primitives étaient double ou long?

Était-ce utile?

La solution

Ça dépend.

Notez cependant que vous devez généralement synchroniser les opérations à un niveau plus grossier, par exemple, ceci:

Example e = ...;

synchronized (e) {
    e.setI(e.getI() + 10);
}

Pour de tels scénarios, vos verrous internes sont redondants. Par conséquent, il serait peut-être préférable d'appliquer une synchronisation externe où vous utilisez ces objets, plutôt que la synchronisation interne.

Autres conseils

Vous avez quelque chose comme AtomicInteger à Java, qui fonctionne bien avec l'application multithread.

Demandez-vous si cette classe peut être mise en œuvre en tant que classe immuable. Il sera plus facile de travailler avec et sera intrinsèquement filé. Vous n'aurez pas à vous soucier de la concurrence, des verrous, de la synchronisation, etc.

Exemple d'une classe immuable:

final class Example {
    private final int i;
    private final boolean b;

    public Example(int i, boolean b){
        this.i = i ;
        this.b = b;
    }

    public int getI() {
        return i;
    }

    public boolean getB() {
        return b;
    }
}

Je concevoirais votre application afin que vous n'ayez pas accès simultanément à un type de données bruts comme celui-ci. L'ajout d'un verrouillage de bas niveau est susceptible de ralentir votre application tellement qu'il ne vaut pas le multiplication multiplié par votre application.

Par exemple, disons que vous avez un système de base 32 qui évolue parfaitement et fonctionne 32x plus rapidement que sur 1 noyau. Cependant, un accès sur le terrain sans verrouillage prend 1 ns, le verrouillage prend 1 US (1000 ns), donc à la fin, votre application pourrait prendre ~ 30x plus lent. (1000 plus lentement / 32 plus rapidement) Si vous n'avez que 4 cœurs, cela pourrait être plus lent, battant le but d'avoir des multiples multiples en premier lieu. A MON HUMBLE AVIS.

Il n'est pas nécessaire de verrouiller / synchroniser les getters et les setteurs de types primitifs - les étiqueter comme volatils est suffisant dans la plupart des cas (à part le double et le long que vous le mentionnez)

Comme l'un des articles précédents le mentionne, vous devez être conscient des séquences de lecture et de mise à jour, quelque chose comme incrementi (int num) qui invoquera probablement geti () et seti () - dans ce cas, vous pourriez ajouter un 'synchronisé incrémentiel (int num) 'Méthode à votre exemple de classe. Le verrouillage se fait ensuite à un niveau supérieur, réduisant le besoin de verrous de lecture et d'écriture séparés et est convivial car les données et le comportement restent ensemble. Cette technique est encore plus utile si vous lisez / mettez à jour plusieurs champs à la fois.

Bien que si vous êtes simplement, lire / écrire / mettre à jour un champ à la fois, alors les classes Atomicxx sont plus appropriées

Vous ne devez pas utiliser les types de verrouillage sur les types primitifs, la chaîne (ils sont immuables) et les types de filetage (comme les collections du package "simultané").

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top