单机上多JVM同步控制的一种方法

在一个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();的测试这里就不在给出具体的情况了。

相关推荐