Le paquetage NIO dispose des outils nécessaire à la mise en place d'un verrouillage de la totalité d'un fichier ou seulement de sa partie sensible. Les verrous constituent un moyen de coordonner le partage et l'acquisition de verrous. Un verrou peut être exclusif ou partagé. :
Classe | Description |
---|---|
FileLock | contient un jeton (token) représentant un verrou sur une zone d'un fichier. |
Les méthodes lock() de la classe FileChannel permet de verrouiller une partie d'un fichier. Le fichier cible peut être verrouillé entièrement (lock()) ou partiellement (lock(in, int, boolean)). Dans ce dernier cas, la délimitation de la partie à verrouiller est indiquée par deux valeurs entières exprimant la position de départ et la taille totale de la zone ciblée.
int debut = 10; int taille = 20; boolean acces_partage = false; RandomAccessFile raf = new RandomAccessFile("fichier.txt", "rw"); FileChannel fc = raf.getChannel(); //La zone ... >> 10 <--> 30 << ... est verrouillée FileLock verrou = fc.lock(debut, taille, acces_partage); //ou //Tout le fichier est verrouillé FileLock verrou = fc.lock(); //Equivaut à fc.lock(0L, Long.MAX_VALUE, false);
L'équivalence à la méthode lock() utilise une taille égale à la valeur maximum des entiers long, car si la taille du fichier augmente, tout son contenu, malgré sa modification, continuera à être l'objet du verrou.
FileLock verrou = fc.lock(0L, fc.size(), false); //Modification du fichier...
//=> taille du fichier est égale à fc.size() + 20 int taille = verrou.size(); //taille est égale à fc.size()
Le verrouillage est maintenant partiel //Verrouillage complet quelque soit les modifications de taille FileLock verrou = fc.lock(0L, Long.MAX_VALUE, false);
La méthode lock(in, int, boolean) possède un troisième paramètre booléen décidant du type du verrou à appliquer à la zone à verrouiller du fichier. Si l'argument spécifié est true, alors le verrou sera partagé. Dans le cas contraire, il sera exclusif.
FileLock verrou_partage = fc.lock(debut, taille, true);
Les méthodes lock() bloque le programme (ou l'un de ses threads) jusqu'à ce que le verrou soit acquis sur le fichier ou que le canal sous-jacent soit fermé ou que le thread invoquant une de ces méthodes soit interrompu. Afin d'éviter cet effet de blocage, il est possible d'invoquer les méthodes tryLock() qui effectuent une tentative d'acquisition d'un verrou et retourne immédiatement un objet FileLock, si le verrouillage est obtenu, ou la valeur null si la commande a échouée.
FileLock verrou = fc.tryLock(); FileLock verrou = fc.tryLock(debut, taille, false);
Plusieurs exceptions peuvent être lancées :
Exception | Méthodes |
---|---|
Description | |
IllegalArgumentException | lock(int, int, boolean) tryLock(int, int, boolean) |
Si les paramètres ne sont pas conformes aux préconditions (position > 0, taille > 0 et taille + position > 0). | |
ClosedChannelException | lock() tryLock() lock(int, int, boolean) tryLock(int, int, boolean) |
Si le canal sous-jacent est fermé. | |
AsynchronousCloseException | lock() tryLock() |
Si un autre thread ferme le canal pendant que le thread invoquant la méthode lock() est bloqué dans cette méthode. | |
FileLockInterruptionException | lock() tryLock() |
Si le thread invoquant la méthode lock() est interrompu au moment bloqué dans cette méthode. | |
OverlappingFileLockException | lock() tryLock() lock(int, int, boolean) tryLock(int, int, boolean) |
Si un verrou qui recouvre la zone requise du fichier est déjà tenu par la JVM, ou si un autre thread est déjà bloqué par cette méthode et tente de verrouiller une zone recouverte du même fichier. | |
NonReadableChannelException | lock() tryLock() |
Si le verrou est partagé et que le canal sous-jacent n'a pas été ouvert pour la lecture. | |
NonWritableChannelException | lock() tryLock() |
Si le verrou n'est pas partagé et que le canal n'a pas été ouvert pour l'écriture. | |
IOException | lock() tryLock() lock(int, int, boolean) tryLock(int, int, boolean) |
Si une erreur d'entrée/sortie se produit. |
Le verrou doit être libéré afin que d'autres programmes concurrents puissent éventuellement acquérir le verrou à leur tour. La méthode release() s'acquitte de cette tâche.
verrou.release();
Toutefois, si le canal associé au fichier est déjà fermé, sera lancée une exception ClosedChannelException si la méthode release() est invoquée.
Par ailleurs, la fermeture du canal (celui récupérable par channel()) ou l'arrêt de la machine virtuelle entraîne une libération automatique du verrou sur le fichier.
fc.close(); if(verrou.channel().isOpen()){ //Si le canal est encore ouvert }
La validité d'un verrou peut être testée par la méthode isValid(). Tant que le verrou n'est pas libéré, la méthode retournera une valeur booléenne true.
boolean actif = verrou.isValid();
La méthode overlaps() vérifie si une partie du fichier est (true) ou n'est pas verroullé (false). Un intervalle exprimé par une position de départ et une taille, est passé en argument à cette méthode.
boolean actif = verrou.overlaps(10, 20); //dans ce cas, actif = true
La position de départ et la taille délimitant la zone d'un fichier verrouillé, sont disponibles à partir des méthodes position() et size().
int position = verrou.position(); int taille = verrou.size(); //position = 10 et taille = 20
Enfin, le type du verrou est déterminable par un appel à la méthode isShared(). En effet, si la méthode précitée retourn true, alors le verrou est partagé sinon, il est exclusif.
Exemple [voir]if(verrou.isShared()){ //Le verrou est partagé }
package nio; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; public class Verrouillage extends Thread { private final int position = 10; private final int taille = 20; private int numero; private int pause; private boolean essai; public static void main(String args[]) { boolean essai = false; if (essai) System.out.println("*** Utilisation de tryLock() ***"); else System.out.println("*** Utilisation de lock() ***"); for (int i = 0; i < 3; i++) { Verrouillage thread = new Verrouillage(i, i + 1, essai); thread.start(); } } public Verrouillage(int numero, int s, boolean essai) { this.numero = numero; this.pause = s * 1000; this.essai = essai; } public void run() { this.executer(); } public void executer() { try { RandomAccessFile raf = new RandomAccessFile("usefilelocks.txt", "rw"); FileChannel fc = raf.getChannel(); System.out.println("Ouverture du canal " + numero); FileLock verrou = null; if (essai) verrou = fc.tryLock(position, taille, false); else verrou = fc.lock(position, taille, false); System.out.println("Acquisition du verrou n°" + numero + " verrou=" + verrou); System.out.println("\tPause de " + (pause / 1000) + "s sur le verrou n°" + numero); try { Thread.sleep(pause); System.out.println("\tFin de la pause sur le verrou n°" + numero); } catch (InterruptedException ie) { } if(verrou != null) { System.out.println("\tLibération du verrou n°" + numero); verrou.release(); } System.out.println("Fermeture du canal " + numero); raf.close(); } catch (IOException e) { e.printStackTrace(); } } } |