3

J'ai 2 threads qui accèdent simultanément au même fichier volumineux (.txt).java Mémoire mappée Fichiers multithread lecture/écriture

1er Le fil lit dans le fichier. 2ème thread écrit dans le fichier.

Les deux threads accèdent au même bloc, par ex. (Début: 0, blocksize: 10), mais avec différents canaux & instances de tampon

Lecteur:

{ 
    int BLOCK_SIZE = 10; 
    byte[] bytesArr = new byte[BLOCK_SIZE]; 
    File file = new File("/db.txt"); 
    RandomAccessFile randomFile = new RandomAccessFile(file, "r"); 
    FileChannel channel = randomFile.getChannel(); 
    MappedByteBuffer map = channel.map(FileChannel.MapMode.READ_ONLY, 0, BLOCK_SIZE); 
    map.get(bytesArr , 0, BLOCK_SIZE); 
    channel.close(); 
} 

Writer:

{ 
    int BLOCK_SIZE = 10; 
    File file = new File("/db.txt"); 
    RandomAccessFile randomFile = new RandomAccessFile(file, "rw"); 
    FileChannel channel = randomFile.getChannel(); 
    MappedByteBuffer map = channel.map(FileChannel.MapMode.READ_WRITE, 0, BLOCK_SIZE); 
    map.put(bytesToWrite); 
    channel.close(); 
} 

Je sais que si les deux commence en même temps, j'obtiendrai des exceptions qui se chevauchent! MAIS ce que je voudrais savoir, à quel moment exactement le chevauchement se produit? Je veux dire quand arrive le "verrou" exactement? Exemple: permet de dire que l'accès get écrivain d'abord, puis si le lecteur essayez d'accéder, à quel point est-il possible ?:

FileChannel channel = randomFile.getChannel(); 
// 1- can reader access here? 
MappedByteBuffer map = channel.map(FileChannel.MapMode.READ_WRITE, 0, BLOCK_SIZE); 
// 2- can reader access here? 
map.put(bytesToWrite); 
// 3- can reader access here? 
channel.close(); 
// 4- can reader access here? 

1, 2, 3 ou 4?

Non 4 est sûr, parce que le canal est fermé! Qu'en est-il des autres points?

Merci!

+0

Je ne vois aucun verrou dans votre code. –

+0

Pourquoi utiliser plusieurs threads? Un aperçu de votre cas d'utilisation nous aiderait à vous conseiller. En général, je recommande d'utiliser un seul thread pour les E/S sauf si une situation très spécialisée s'est produite. –

+0

@ChrisK, je pourrais vous donner un cas d'utilisation, mais connaissez-vous JSF ManagedBeans? –

Répondre

2

Je résume quelques notes d'une conversation par chat avec l'OP. L'OP avait le modèle mental (comme la plupart d'entre nous) qu'une fois qu'un thread écrit dans une structure de données, cette structure de données est immédiatement visible pour tous les autres threads. Dans les tests OP utilisant des fichiers mappés en mémoire, il avait confirmé que cela semblait être vrai sur un processeur Intel à une seule socket.

Malheureusement, cela n'est pas vrai et c'est un domaine dans lequel Java peut montrer le comportement sous-jacent du matériel. Java a été conçu pour supposer que le code est à un seul thread, et peut donc être optimisé en tant que tel jusqu'à ce qu'on le lui dise autrement. Ce que cela signifie diffère selon le matériel et la version de hotspot (et les statistiques recueillies par hotspot). Cette complexité, et s'exécutant sur un seul processeur Intel socket invalidé le test OPs. Pour plus d'informations, les liens suivants vous aideront à mieux comprendre le «modèle de mémoire Java». Et en particulier que synchronisé ne signifie pas seulement «exclusion mutuelle»; en termes de matériel, il s'agit aussi de «visibilité des données» et de «commande d'instruction». Deux sujets que le code à un seul thread prend pour acquis.

Ne vous inquiétez pas si cela prend du temps à couler, et que vous vous sentez dépassés dans un premier temps. Nous nous sentions tous comme ça au début.Java fait un travail incroyable de cacher cette complexité, si et seulement si vous suivez cette règle simple. Lorsqu'un thread lit ou modifie une structure de données partagée, il doit être dans un bloc synchronisé. C'est-à-dire, à la fois le fil d'écriture et le fil de lecture. Évidemment, je simplifie, mais suivez cette règle et le programme fonctionnera toujours. Ne le cassez que si vous avez une très bonne compréhension du modèle de mémoire Java, des barrières de la mémoire et de son lien avec différents matériels (même les experts en simultanéité évitent même d'enfreindre cette règle s'ils le peuvent). peut être surprisingly fast .. de nombreux systèmes à faible latence sont conçus pour être la plupart du temps à simple thread pour cette raison).


Pour répondre directement à la question OPs. L'exemple de code de la question n'a pas de verrous. Aucune barrière de mémoire, aucun contrôle de concurrence. Ainsi, le comportement de l'interaction entre les lectures et les écritures n'est pas défini. Ils peuvent travailler, ils ne peuvent pas. Ils peuvent travailler la plupart du temps. Intel a les meilleures garanties de mémoire de tous les processeurs, et en exécutant les cas de test sur un seul socket, le processeur Intel manquerait beaucoup de bogues complexes. Sun a été surpris par cela aussi avant que Java 5 et JSR 133 soient sortis (lire l'article sur pourquoi Double Checked Locking a été cassé en Java pour plus de détails).

+0

je vous remercie beaucoup pour votre aide. les liens que vous avez publiés à propos de Java Memory Model et Memory Barrier sont très utiles. malgré le fait que je demandais de conccurenry en lisant/écrivant le même bloc d'octets de/vers MappedByteBuffers et toutes les suggestions/réponses allant dans une autre direction (peut-être en raison de ma mauvaise question), mais cela m'a conduit au fait que je dois lire plus sur l'interaction entre JVM, mémoire, système et matériel. –

+0

Cette réponse est-elle vraiment valable? Autant que je sache, les fichiers mappés en mémoire sont spéciaux. Ils sont gérés par l'os, par exemple mmap sur le système Posix. Ils garantissent un comportement spécial. Par exemple, si vous changez un bit, une erreur de page est générée et l'os échange cette page du disque à la mémoire et vice versa. – slowjack2k

1

Vous ne recevrez pas aucune exceptions de verrouillage de ce code, ou des blocs non plus. Les verrous de fichiers fonctionnent entre les processus et non entre les threads. Ce dont vous avez besoin ici, c'est la synchronisation, ou les sémaphores, ou ReadWriteLocks. Et il n'y a pas besoin d'utiliser deux canaux.

+0

merci pour votre réponse, mais pourriez-vous me donner une Usecase où Chevauchement se produit? –

+0

@ Rami.Q Que voulez-vous dire par chevauchement? En n'utilisant aucune forme de barrière de mémoire dans votre code concurrent, vous n'aurez aucune idée de ce qui sera visible dans le fil de lecture. Il peut s'agir des données qui ont été écrites, il peut s'agir des données avant leur écriture ou des données partiellement écrites et donc corrompues. –