FileLock 多进程文件锁

简介: FileLock 多进程文件锁

FileLock是文件锁,它能保证同一时间只有一个进程(程序)能够修改它,或者都只可以读,这样就解决了多进程间的同步文件,保证了安全性。但是需要注意的是,它进程级别的,不是线程级别的,他可以解决多个进程并发访问同一个文件的问题,但是它不适用于控制同一个进程中多个线程对一个文件的访问。这也是为什么它叫做 多进程文件锁,而不是 多线程文件锁

FileLock一般都是从FileChannel 中获取,FileChannel 提供了三个方法用以获取 FileLock。

  • lock() 是阻塞式的,它要阻塞进程直到锁可以获得,或调用lock()的线程中断,或调用lock()的通道关闭。
  • tryLock()是非阻塞式的,它设法获取锁,但如果不能获得,例如因为其他一些进程已经持有相同的锁,而且不共享时,它将直接从方法调用返回。

    position:锁定文件中的开始位置
    size:锁定文件中的内容长度
    shared:是否使用共享锁。true为共享锁;false为独占锁。
/**
 * 先运行
 * @throws Exception
*/
@Test
public void FileWriteTest() throws Exception {
    RandomAccessFile randomAccessFile = new RandomAccessFile("D:\\temp\\filelock.txt", "rw");
    FileChannel fileChannel = randomAccessFile.getChannel(); 
    System.out.println("进程 1 开始写内容:" + LocalTime.now());
    for (int i = 1; i <= 10; i++) {
        randomAccessFile.writeUTF("VipSoft_" + i);
        System.out.println("writeChars:" + i);
        // 等待两秒
        TimeUnit.SECONDS.sleep(2);
    }
    System.out.println("进程 1 完成写内容:" + LocalTime.now()); 
    fileChannel.close();
    randomAccessFile.close();
}
/**
 * 再运行,看读出文件大小的变化
 * @throws Exception
*/
@Test
public void FileReadTest() throws Exception {
    RandomAccessFile randomAccessFile = new RandomAccessFile("D:\\temp\\filelock.txt", "rw");
    FileChannel fileChannel = randomAccessFile.getChannel(); 
    System.out.println("开始读文件的时间:" + LocalTime.now());
    for (int i = 0; i < 10; i++) {
        // FileWriteTest() 运行后,运行 FileReadTest(),发现文件大小在变。说明文件在一边读一边写
        System.out.println("文件大小为:" + randomAccessFile.length());
        // 这里等待 1 秒
        TimeUnit.SECONDS.sleep(1);
    }
    System.out.println("结束读文件的时间:" + LocalTime.now()); 
    fileChannel.close();
    randomAccessFile.close();
}

使用文件锁,使用后,只能等锁释放掉以后,另一个进程才能对期进行操作

/**
 * 先运行
 * @throws Exception
*/
@Test
public void FileWriteTest() throws Exception {
    RandomAccessFile randomAccessFile = new RandomAccessFile("D:\\temp\\filelock.txt", "rw");
    FileChannel fileChannel = randomAccessFile.getChannel();
    // 这里是独占锁
    FileLock fileLock = fileChannel.lock();
    System.out.println("进程 1 开始写内容:" + LocalTime.now());
    for (int i = 1; i <= 10; i++) {
        randomAccessFile.writeUTF("VipSoft_" + i);
        System.out.println("writeChars:" + i);
        // 等待两秒
        TimeUnit.SECONDS.sleep(2);
    }
    System.out.println("进程 1 完成写内容:" + LocalTime.now());
    // 完成后要释放掉锁
    fileLock.release();
    fileChannel.close();
    randomAccessFile.close();
}
/**
 * 再运行,看读出文件大小的变化
 * @throws Exception
*/
@Test
public void FileReadTest() throws Exception {
    RandomAccessFile randomAccessFile = new RandomAccessFile("D:\\temp\\filelock.txt", "rw");
    FileChannel fileChannel = randomAccessFile.getChannel();
    // 这里是独占锁
    FileLock fileLock = fileChannel.lock();
    System.out.println("开始读文件的时间:" + LocalTime.now());
    for (int i = 0; i < 10; i++) {
        // FileWriteTest() 运行后,运行 FileReadTest(),发现文件大小在变。说明文件在一边读一边写
        System.out.println("文件大小为:" + randomAccessFile.length());
        // 这里等待 1 秒
        TimeUnit.SECONDS.sleep(1);
    }
    System.out.println("结束读文件的时间:" + LocalTime.now());
    // 完成后要释放掉锁
    fileLock.release();
    fileChannel.close();
    randomAccessFile.close();
}

同进程不同线程进行文件读写

FileLock是不适用同一进程不同线程之间文件的访问。因为你根本无法在一个进程中不同线程同时对一个文件进行加锁操作,如果线程1对文件进行了加锁操作,这时线程2也来进行加锁操作的话,则会直接抛出异常:java.nio.channels.OverlappingFileLockException

import org.junit.Test;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.time.LocalTime;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class FileLockTest {
    @Test
    public void RunnableTest()  throws InterruptedException {
        Runner1 runner1 = new Runner1();
        Runner2 runner2 = new Runner2();
        Thread thread1 = new Thread(runner1);
        Thread thread2 = new Thread(runner2);
        thread1.start();
        thread2.start();
        System.out.print("阻塞当前线程,直到倒数计数器倒数到0");
        new CountDownLatch(1).await();
    }
    class Runner1 implements Runnable { // 实现了Runnable接口,jdk就知道这个类是一个线程
        public void run() {
            try {
                FileWriteTest();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    class Runner2 implements Runnable { // 实现了Runnable接口,jdk就知道这个类是一个线程
        public void run() {
            try {
                FileReadTest();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 先运行
     * @throws Exception
     */
    public void FileWriteTest() throws Exception {
        RandomAccessFile randomAccessFile = new RandomAccessFile("D:\\temp\\filelock.txt", "rw");
        FileChannel fileChannel = randomAccessFile.getChannel();
        // 这里是独占锁
        FileLock fileLock = fileChannel.lock();
        System.out.println("进程 1 开始写内容:" + LocalTime.now());
        for (int i = 1; i <= 10; i++) {
            randomAccessFile.writeUTF("VipSoft_" + i);
            System.out.println("writeChars:" + i);
            // 等待两秒
            TimeUnit.SECONDS.sleep(2);
        }
        System.out.println("进程 1 完成写内容:" + LocalTime.now());
        // 完成后要释放掉锁
        fileLock.release();
        fileChannel.close();
        randomAccessFile.close();
    }
    /**
     * 再运行,看读出文件大小的变化
     * @throws Exception
     */
    @Test
    public void FileReadTest() throws Exception {
        RandomAccessFile randomAccessFile = new RandomAccessFile("D:\\temp\\filelock.txt", "rw");
        FileChannel fileChannel = randomAccessFile.getChannel();
        // 这里是独占锁
        FileLock fileLock = fileChannel.lock();
        System.out.println("开始读文件的时间:" + LocalTime.now());
        for (int i = 0; i < 10; i++) {
            // FileWriteTest() 运行后,运行 FileReadTest(),发现文件大小在变。说明文件在一边读一边写
            System.out.println("文件大小为:" + randomAccessFile.length());
            // 这里等待 1 秒
            TimeUnit.SECONDS.sleep(1);
        }
        System.out.println("结束读文件的时间:" + LocalTime.now());
        // 完成后要释放掉锁
        fileLock.release();
        fileChannel.close();
        randomAccessFile.close();
    }
}

可以通过另外一种方式来规避程序异常,如下:

FileLock fileLock;
while (true){
    try{
        fileLock = fileChannel.tryLock();
        break;
    } catch (Exception e) {
        System.out.println("这里是 FileWriteTest ,其他线程已经获取该文件锁了,当前线程休眠 2 秒再获取");
        TimeUnit.SECONDS.sleep(2);
    }
}

完整DEMO如下

package org.apache.rocketmq.store;
import org.junit.Test;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.time.LocalTime;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class FileLockTest {
    @Test
    public void RunnableTest()  throws InterruptedException {
        Runner1 runner1 = new Runner1();
        Runner2 runner2 = new Runner2();
        Thread thread1 = new Thread(runner1);
        Thread thread2 = new Thread(runner2);
        thread1.start();
        thread2.start();
        System.out.print("阻塞当前线程,直到倒数计数器倒数到0");
        new CountDownLatch(1).await();
    }
    class Runner1 implements Runnable {
        public void run() {
            try {
                FileWriteTest();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    class Runner2 implements Runnable {
        public void run() {
            try {
                FileReadTest();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 先运行
     * @throws Exception
     */
    public void FileWriteTest() throws Exception {
        RandomAccessFile randomAccessFile = new RandomAccessFile("D:\\temp\\filelock.txt", "rw");
        FileChannel fileChannel = randomAccessFile.getChannel();
        // 这里是独占锁
        FileLock fileLock;
        while (true){
            try{
                fileLock = fileChannel.tryLock();
                break;
            } catch (Exception e) {
                System.out.println("这里是 FileWriteTest ,其他线程已经获取该文件锁了,当前线程休眠 2 秒再获取");
                TimeUnit.SECONDS.sleep(2);
            }
        }
        System.out.println("进程 1 开始写内容:" + LocalTime.now());
        for (int i = 1; i <= 10; i++) {
            randomAccessFile.writeUTF("VipSoft_" + i);
            System.out.println("writeChars:" + i);
            // 等待两秒
            TimeUnit.SECONDS.sleep(2);
        }
        System.out.println("进程 1 完成写内容:" + LocalTime.now());
        // 完成后要释放掉锁
        fileLock.release();
        fileChannel.close();
        randomAccessFile.close();
    }
    /**
     * 再运行,看读出文件大小的变化
     * @throws Exception
     */
    @Test
    public void FileReadTest() throws Exception {
        RandomAccessFile randomAccessFile = new RandomAccessFile("D:\\temp\\filelock.txt", "rw");
        FileChannel fileChannel = randomAccessFile.getChannel();
        // 这里是独占锁
        FileLock fileLock;
        while (true){
            try{
                fileLock = fileChannel.tryLock();
                break;
            } catch (Exception e) {
                System.out.println("这里是 FileReadTest,其他线程已经获取该文件锁了,当前线程休眠 2 秒再获取");
                TimeUnit.SECONDS.sleep(2);
            }
        }
        System.out.println("开始读文件的时间:" + LocalTime.now());
        for (int i = 0; i < 10; i++) {
            // FileWriteTest() 运行后,运行 FileReadTest(),发现文件大小在变。说明文件在一边读一边写
            System.out.println("文件大小为:" + randomAccessFile.length());
            // 这里等待 1 秒
            TimeUnit.SECONDS.sleep(1);
        }
        System.out.println("结束读文件的时间:" + LocalTime.now());
        // 完成后要释放掉锁
        fileLock.release();
        fileChannel.close();
        randomAccessFile.close();
    }
}

 

目录
相关文章
聊聊python多线程与多进程
为什么要使用多进程与多线程呢? 因为我们如果按照流程一步步执行任务实在是太慢了,假如一个任务就是10秒,两个任务就是20秒,那100个任务呢?况且cpu这么贵,时间长了就是浪费生命啊!一个任务比喻成一个人,别个做高铁,你做绿皮火车,可想而知!接下来我们先看个例子:
|
9月前
文件锁的应用
文件锁的应用
34 0
|
12月前
|
NoSQL 关系型数据库 MySQL
多进程与多线程
多进程与多线程
80 0
|
Java C++ Python
Python多线程与多进程
全局解释器锁(GIL)导致了Python多线程无法利用多核CPU并发执行。引入GIL,是为了解决多线程之间数据完整性和状态同步的问题,简化了Python对共享资源的管理;但是也降低了并发编程的性能。
16765 3
Python多线程与多进程
|
Go 调度 Python
多线程与多进程(一)
多线程与多进程
100 0
多线程与多进程(一)
|
Java C语言 Python
多线程与多进程(三)
多线程与多进程
117 0
多线程与多进程(三)
|
Unix Linux 调度
|
Android开发 数据安全/隐私保护 C++
多线程、多进程同时操作MMAP,会怎么样?(一)
多线程、多进程同时操作MMAP,会怎么样?
272 0
多线程、多进程同时操作MMAP,会怎么样?(一)
|
消息中间件 Web App开发 并行计算
彻底理解 进程、线程、多进程、多线程
面试经常被问到的问题,今天给大家总结一下📌
158 0
彻底理解 进程、线程、多进程、多线程
|
Linux
多线程、多进程同时操作MMAP,会怎么样?(二)
多线程、多进程同时操作MMAP,会怎么样?
352 0