Question

J'ai toujours pensé que la synchronisation de la méthode d'exécution dans une classe java qui implémente Runnable est redondant. Je suis en train de comprendre pourquoi les gens font ceci:

public class ThreadedClass implements Runnable{
    //other stuff
    public synchronized void run(){
        while(true)
             //do some stuff in a thread
        }
    }
}

Il semble redondant et inutile car ils obtiennent le verrou pour un autre thread de l'objet. Ou plutôt, ils font explicitement qu'un seul thread a accès à la méthode run (). Mais depuis sa la méthode d'exécution, est-il pas lui-même son propre fils? Par conséquent, elle seule peut lui-même accès et il n'a pas besoin d'un mécanisme de verrouillage séparé?

J'ai trouvé une suggestion en ligne que par la synchronisation de la méthode d'exécution, vous pouvez potentiellement créer une file d'attente de fil de fait, par exemple, en faisant ceci:

 public void createThreadQueue(){
    ThreadedClass a = new ThreadedClass();
    new Thread(a, "First one").start();
    new Thread(a, "Second one, waiting on the first one").start();
    new Thread(a, "Third one, waiting on the other two...").start();
 }

Je ne ferais jamais cela personnellement, mais il prête à la question de savoir pourquoi quelqu'un voudrait synchroniser la méthode d'exécution. Toutes les idées pourquoi ou pourquoi pas on doit synchroniser la méthode d'exécution?

Était-ce utile?

La solution

Synchronisation de la run() d'une méthode Runnable est complètement inutile à moins que que vous souhaitez partager la Runnable entre plusieurs threads et vous voulez séquentialiser l'exécution de ces discussions. Ce qui est fondamentalement une contradiction dans les termes.

Il est en théorie un autre scénario beaucoup plus compliqué dans lequel vous pouvez synchroniser la méthode run(), ce qui implique à nouveau partager la Runnable entre plusieurs threads, mais permet également l'utilisation de wait() et notify(). Je ne l'ai jamais rencontré dans 21+ années de Java.

Autres conseils

Il y a 1 avantage d'utiliser synchronized void blah() sur void blah() { synchronized(this) { et qui est votre bytecode résultant sera 1 octet plus courte, puisque la synchronisation fera partie de la signature de la méthode au lieu d'une opération en elle-même. Cela peut influencer la chance de inline la méthode par le compilateur JIT. Autre que qu'il n'y a pas de différence.

La meilleure option est d'utiliser un private final Object lock = new Object() interne pour empêcher quelqu'un de bloquer potentiellement votre moniteur. Elle permet d'obtenir le même résultat sans l'inconvénient du mal verrouillage extérieur. Vous avez cet octet supplémentaire, mais il fait rarement la différence.

Je dirais non, ne pas utiliser le mot-clé synchronized dans la signature. Au lieu de cela, utiliser quelque chose comme

public class ThreadedClass implements Runnable{
    private final Object lock = new Object();

    public void run(){
        synchronized(lock) {
            while(true)
                 //do some stuff in a thread
            }
        }
    }
}

Modifier en réponse à un commentaire:

Considérez ce que la synchronisation ne: il empêche les autres threads d'entrer dans le même bloc de code. Alors, imaginez que vous avez une classe comme celle ci-dessous. Disons que la taille actuelle est 10. Quelqu'un tente d'effectuer un complément et il force un redimensionnement du tableau de support. Alors qu'ils sont au milieu de redimensionner le tableau, quelqu'un appelle un makeExactSize(5) sur un thread différent. Maintenant, tout d'un coup vous essayez de data[6] d'accès et il bombes sur vous. La synchronisation est censé empêcher que cela se produise. Dans les programmes multithread il vous suffit de synchronisation NEED.

class Stack {
    int[] data = new int[10];
    int pos = 0;

    void add(int inc) {
        if(pos == data.length) {
            int[] tmp = new int[pos*2];
            for(int i = 0; i < pos; i++) tmp[i] = data[i];
            data = tmp;
        }
        data[pos++] = inc;
    }

    int remove() {
        return data[pos--];
    }

    void makeExactSize(int size) {
        int[] tmp = new int[size];
        for(int i = 0; i < size; i++) tmp[i] = data[i];
        data = tmp;
    }
}

Pourquoi? Un minimum de sécurité supplémentaire et je ne vois pas de scénario plausible où il ferait une différence.

Pourquoi pas? Ce n'est pas standard. Si vous codez dans le cadre d'une équipe, quand un autre membre voit votre run synchronisé, il va probablement perdre 30 minutes à essayer de comprendre ce qui est si spécial soit avec votre run ou avec le cadre que vous utilisez pour exécuter de la Runnable.

D'après mon expérience, il est inutile d'ajouter mot-clé « synchronisé » à la méthode d'exécution (). Si nous avons besoin de synchroniser plusieurs threads, ou nous avons besoin d'une file d'attente de thread-safe, on peut utiliser des composants plus appropriés, tels que ConcurrentLinkedQueue.

Eh bien, vous pourriez théoriquement appeler la méthode d'exécution lui-même sans problème (après tout, il est public). Mais cela ne veut pas dire qu'on devrait le faire. Donc, fondamentalement, il n'y a aucune raison de le faire, à part rallongeant négligeable au terme d'appel de fil (). Eh bien, sauf si vous utilisez le temps par exemple de multiples appels new Thread - bien que je suis) pas sûr que ce droit avec l'API de filetage et b) semble complètement inutile

.

Aussi votre createThreadQueue ne fonctionne pas. synchronized sur une méthode non statique synchronise sur l'objet d'instance (par exemple this), de sorte que les trois fils vont fonctionner en parallèle.

Passez par les commentaires du code et uncomment et exécuter les différents blocs pour voir clairement la différence, la synchronisation des notes aura une différence que si la même instance runnable est utilisé, si chaque thread commencé obtient un nouveau runnable il ne fera aucune différence.

class Kat{

public static void main(String... args){
  Thread t1;
  // MyUsualRunnable is usual stuff, only this will allow concurrency
  MyUsualRunnable m0 = new MyUsualRunnable();
  for(int i = 0; i < 5; i++){
  t1 = new Thread(m0);//*imp*  here all threads created are passed the same runnable instance
  t1.start();
  }

  // run() method is synchronized , concurrency killed
  // uncomment below block and run to see the difference

  MySynchRunnable1 m1 = new MySynchRunnable1();
  for(int i = 0; i < 5; i++){
  t1 = new Thread(m1);//*imp*  here all threads created are passed the same runnable instance, m1
  // if new insances of runnable above were created for each loop then synchronizing will have no effect

  t1.start();
}

  // run() method has synchronized block which lock on runnable instance , concurrency killed
  // uncomment below block and run to see the difference
  /*
  MySynchRunnable2 m2 = new MySynchRunnable2();
  for(int i = 0; i < 5; i++){
  // if new insances of runnable above were created for each loop then synchronizing will have no effect
  t1 = new Thread(m2);//*imp*  here all threads created are passed the same runnable instance, m2
  t1.start();
}*/

}
}

class MyUsualRunnable implements Runnable{
  @Override
  public void  run(){
    try {Thread.sleep(1000);} catch (InterruptedException e) {}
}
}

class MySynchRunnable1 implements Runnable{
  // this is implicit synchronization
  //on the runnable instance as the run()
  // method is synchronized
  @Override
  public synchronized void  run(){
    try {Thread.sleep(1000);} catch (InterruptedException e) {}
}
}

class MySynchRunnable2 implements Runnable{
  // this is explicit synchronization
  //on the runnable instance
  //inside the synchronized block
  // MySynchRunnable2 is totally equivalent to MySynchRunnable1
  // usually we never synchronize on this or synchronize the run() method
  @Override
  public void  run(){
    synchronized(this){
    try {Thread.sleep(1000);} catch (InterruptedException e) {}
  }
}
}

en fait il est vraiment facile de justifier « la synchronisation ou non synchronisation »

si votre appel de méthode peut mute l'état interne de votre objet, puis « sync » sinon pas besoin

exemple simple

public class Counter {

  private int count = 0; 

  public void incr() {
    count++;
  }

  public int getCount() {
    return count;
  }
}

dans l'exemple ci-dessus, INCR () doit être synchronisé, car il changera le val de comptage, tandis que la synchronisation getCount () n'est pas nécessaire

Mais il y a un autre cas d'angle, si le compte est java.lang.Long, Double, objet, vous devez déclarer

private volatile long count = 0;

pour vous assurer que la mise à jour ref est atomique

essentiellement que ce que vous devez penser à 99% du temps lorsqu'ils traitent avec multithreading

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