在一個JVM進程中的同步控制,無非就是線程之間的同步問題,但要想在多JVM進程之間實現訪問本地系統資源的同步,卻並非JVM所擅長的了。當然,若依賴第三方Master主控進程,這個問題還是可以解決的。多於簡單的系統而言,這樣做無疑會增加系統的復雜性,從而導致整個系統的不穩定性和潛在的不安全。所以,下面就如何在單機上實現多JVM進程之間的同步控制,給出了一個簡單的方法。
JDK 1.4的文件鎖"file locking"允許你以文件為共享資源,對訪問進行同步化處理(allows you to synchronize access to a file as a shared resource)。但是,競爭文件的兩個線程必須屬於兩個不同的JVM,或者一個是Java線程,另一個是操作系統的本地線程。由於Java的文件鎖是直接映射操作系統的鎖機制的,因此其它進程也能看到文件鎖。
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
public class TestRandomAccessFile {
public static void testTryLock(){
RandomAccessFile f1=null;
RandomAccessFile f2=null;
FileChannel channel1=null;
FileChannel channel2=null;
FileLock lock1=null;
FileLock lock2=null;
try {
f1=new RandomAccessFile("test.txt","rw");
//f1.writeChars("asd");
channel1 = f1.getChannel();
lock1 = channel1.tryLock();
if(lock1!=null) System.out.println("1:success");
else System.out.println("1:fail");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
f2=new RandomAccessFile("test.txt","rw");
//f2.writeChars("123");
channel2 = f1.getChannel();
lock2 = channel2.tryLock();
if(lock2!=null) System.out.println("1:success");
else System.out.println("1:fail");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
if(lock1!=null) lock1.release();
if(lock2!=null) lock1.release();
if(channel1!=null) channel1.close();
if(channel2!=null) channel2.close();
if(f1!=null) f1.close();
if(f2!=null) f2.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void testLock(){
RandomAccessFile f1=null;
RandomAccessFile f2=null;
FileChannel channel1=null;
FileChannel channel2=null;
FileLock lock1=null;
FileLock lock2=null;
try {
f1=new RandomAccessFile("test.txt","rw");
//f1.writeChars("asd");
channel1 = f1.getChannel();
lock1 = channel1.lock();
if(lock1!=null) System.out.println("1:success");
else System.out.println("1:fail");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
f2=new RandomAccessFile("test.txt","rw");
//f2.writeChars("123");
channel2 = f1.getChannel();
lock2 = channel2.lock();
if(lock2!=null) System.out.println("1:success");
else System.out.println("1:fail");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
if(lock1!=null) lock1.release();
if(lock2!=null) lock1.release();
if(channel1!=null) channel1.close();
if(channel2!=null) channel2.close();
if(f1!=null) f1.close();
if(f2!=null) f2.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
testTryLock();
// testLock();
}
}
��接運行會出現:
1:success
Exception in thread "main" java.nio.channels.OverlappingFileLockException
at sun.nio.ch.FileChannelImpl$SharedFileLockTable.checkList(Unknown Source)
at sun.nio.ch.FileChannelImpl$SharedFileLockTable.add(Unknown Source)
at sun.nio.ch.FileChannelImpl.lock(Unknown Source)
at java.nio.channels.FileChannel.lock(Unknown Source)
at test.TestRandomAccessFile.testLock(TestRandomAccessFile.java:107)
at test.TestRandomAccessFile.main(TestRandomAccessFile.java:147)
一個JVM運行一半,另一個運行全部,
運行一半的 JVM 會出現:
1:success
運行全部的 JVM 會出現:
1:fail
1:fail
要想獲取整個文件的鎖,可以用FileChannel的tryLock( )或lock( )方法。(SocketChannel,DatagramChannel,以及 ServerSocketChannel是不需要鎖的,因為它們原本就是"進程實體(single-pricess entities)";一般來說,你是不會讓兩個進程去共享一個網絡socket的。)tryLock( ) 是非阻塞的。它會試著去獲取這個鎖,但是如果得不到(其它進程已經以獨占方式得到這個鎖了),那它就直接返回。而lock( )是阻塞的。如果得不到鎖,它會在一直處於阻塞狀態,除非它得到了鎖,或者你打斷了調用它(即lock( )方法)的線程,或者關閉了它要lock( )的channel,否則它是不會返回的。最後用FileLock.release( )釋放鎖。
關於 testLock();的測試這裡就不在給出具體的情況了。