2010-10-01 8 views
4

Je lisais environ CopyOnWriteArrayList et je me demandais comment je peux démontrer la course de données dans la classe ArrayList. Fondamentalement, j'essaie de simuler une situation où ArrayList échoue de sorte qu'il devient nécessaire d'utiliser CopyOnWriteArrayList. Toutes les suggestions sur la façon de simuler cela.Course de données dans la classe Java ArrayList

Répondre

6

Une course est lorsque deux (ou plus) threads essaient de fonctionner sur des données partagées, et la sortie finale dépend de l'ordre des données est accessible (et cet ordre est indéterministe)

de Wikipédia:

Une condition de course ou un risque de course est une faille dans un système ou un processus électronique où la sortie et/ou le résultat du processus dépend de façon inattendue et critique de la séquence ou du moment des autres événements. Le terme provient de l'idée de deux signaux qui se courent l'un l'autre pour influencer la sortie en premier.

Par exemple:

public class Test { 
    private static List<String> list = new CopyOnWriteArrayList<String>(); 

    public static void main(String[] args) throws Exception { 
     ExecutorService e = Executors.newFixedThreadPool(5); 
     e.execute(new WriterTask()); 
     e.execute(new WriterTask()); 
     e.execute(new WriterTask()); 
     e.execute(new WriterTask()); 
     e.execute(new WriterTask()); 

     e.awaitTermination(20, TimeUnit.SECONDS); 
    } 

    static class WriterTask implements Runnable { 

     @Override 
     public void run() { 
      for (int i = 0; i < 25000; i ++) { 
       list.add("a"); 
      } 
     } 
    } 
} 

Ceci, cependant, échoue lors de l'utilisation ArrayList, avec ArrayIndexOutOfbounds. En effet, avant l'insertion, le ensureCapacity(..) doit être appelé pour s'assurer que le tableau interne peut contenir les nouvelles données. Et voici ce qui se passe:

  • le premier thread appelle add(..), qui appelle à son tour ensureCapacity(currentSize + 1)
  • avant le premier fil est en fait incrémentée la taille, le 2ème thread appelle également ensureCapacity(currentSize + 1).
  • parce que les deux ont lu la valeur initiale de currentSize, la nouvelle taille du tableau interne est currentSize + 1
  • les deux fils faire l'opération coûteuse pour copier le vieux tableau dans une étendue, avec la nouvelle taille (qui ne peut pas tenir les deux ajouts)
  • Ensuite, chacun d'eux tente d'affecter le nouvel élément à array[size++]. Le premier réussit, le second échoue, car le tableau interne n'a pas été étendu correctement, en raison de la condition de réception.

Cela se produit parce que deux fils ont essayé d'ajouter des éléments en même temps sur la même structure, et l'ajout d'un d'entre eux a surchargé l'ajout de l'autre (la première a été perdue)

un autre avantage de CopyOnWriteArrayList

  • plusieurs threads écrire dans le ArrayList
  • un fil parcourt le ArrayList.Il va sûrement se ConcurrentModificationException

Voici comment le démontrer:

public class Test { 
    private static List<String> list = new ArrayList<String>(); 

    public static void main(String[] args) throws Exception { 
     ExecutorService e = Executors.newFixedThreadPool(2); 
     e.execute(new WriterTask()); 
     e.execute(new ReaderTask()); 
    } 

    static class ReaderTask implements Runnable { 
     @Override 
     public void run() { 
      while (true) { 
       for (String s : list) { 
        System.out.println(s); 
       } 
      } 
     } 
    } 

    static class WriterTask implements Runnable { 
     @Override 
     public void run() { 
      while(true) { 
       list.add("a"); 
      } 
     } 
    } 
} 

Si vous exécutez ce programme plusieurs fois, vous serez souvent obtiendrais ConcurrentModificationExceptionavant vous OutOfMemoryError.

Si vous remplacez par CopyOnWriteArrayList, vous ne recevez pas l'exception (mais le programme est très lent)

Notez que ceci est juste une démonstration - le bénéfice de CopyOnWriteArrayList est lorsque le nombre de lectures considérablement numéros le nombre d'écritures.

+1

@Mark - mais la question concerne le multithreading, et cette exception (comme son nom l'indique) est intrinsèquement "multithread". – Bozho

+0

On dirait que je n'ai pas tout à fait compris. Pouvez-vous nous dire comment les courses de données sont causées? – devnull

+0

@iJeeves voir mis à jour – Bozho

-2

Deux threads, l'un incrémentant l'arraylist et l'autre décrémentant. La course de données pourrait arriver ici.

0

Exemple:

for (int i = 0; i < array.size(); ++i) { 
    Element elm = array.get(i); 
    doSomethingWith(elm); 
} 

Si un autre thread appelle array.clear() avant ce appels fil array.get (i), mais après i est comparé à array.size(), -> ArrayIndexOutOfBoundsException.