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();
    }
}

 

目录
相关文章
|
27天前
|
Python
多进程同步之文件锁
【10月更文挑战第16天】文件锁是一种常用的多进程同步机制,它可以用于确保多个进程在访问共享资源时的互斥性。在使用文件锁时,需要注意锁的粒度、释放、竞争和性能等问题。通过合理使用文件锁,可以提高多进程程序的正确性和性能
|
C语言
进程间通信:文件锁
int flock(int fd, int operation); 其中,参数 fd 表示文件描述符;参数 operation 指定要进行的锁操作,该参数的取值有如下几种:LOCK_SH, LOCK_EX, LOCK_UN 和 LOCK_MAND LOCK_SH:表示要创建一个共享锁,...
1085 0
5进程间锁:进程间pthread_mutex,文件锁
 1进程间pthread_mutex A依赖的头文件 #include&lt;pthread.h&gt; B函数声明 int pthread_mutexattr_destroy(pthread_mutexattr_t *attr); int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr,
1281 0
|
6月前
|
监控 Linux 应用服务中间件
探索Linux中的`ps`命令:进程监控与分析的利器
探索Linux中的`ps`命令:进程监控与分析的利器
129 13
|
5月前
|
运维 关系型数据库 MySQL
掌握taskset:优化你的Linux进程,提升系统性能
在多核处理器成为现代计算标准的今天,运维人员和性能调优人员面临着如何有效利用这些处理能力的挑战。优化进程运行的位置不仅可以提高性能,还能更好地管理和分配系统资源。 其中,taskset命令是一个强大的工具,它允许管理员将进程绑定到特定的CPU核心,减少上下文切换的开销,从而提升整体效率。
掌握taskset:优化你的Linux进程,提升系统性能
|
5月前
|
弹性计算 Linux 区块链
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
177 4
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
|
4月前
|
算法 Linux 调度
探索进程调度:Linux内核中的完全公平调度器
【8月更文挑战第2天】在操作系统的心脏——内核中,进程调度算法扮演着至关重要的角色。本文将深入探讨Linux内核中的完全公平调度器(Completely Fair Scheduler, CFS),一个旨在提供公平时间分配给所有进程的调度器。我们将通过代码示例,理解CFS如何管理运行队列、选择下一个运行进程以及如何对实时负载进行响应。文章将揭示CFS的设计哲学,并展示其如何在现代多任务计算环境中实现高效的资源分配。
|
5月前
|
存储 缓存 安全
【Linux】冯诺依曼体系结构与操作系统及其进程
【Linux】冯诺依曼体系结构与操作系统及其进程
171 1
|
5月前
|
小程序 Linux
【编程小实验】利用Linux fork()与文件I/O:父进程与子进程协同实现高效cp命令(前半文件与后半文件并行复制)
这个小程序是在文件IO的基础上去结合父子进程的一个使用,利用父子进程相互独立的特点实现对数据不同的操作
107 2
|
5月前
|
SQL 自然语言处理 网络协议
【Linux开发实战指南】基于TCP、进程数据结构与SQL数据库:构建在线云词典系统(含注册、登录、查询、历史记录管理功能及源码分享)
TCP(Transmission Control Protocol)连接是互联网上最常用的一种面向连接、可靠的、基于字节流的传输层通信协议。建立TCP连接需要经过著名的“三次握手”过程: 1. SYN(同步序列编号):客户端发送一个SYN包给服务器,并进入SYN_SEND状态,等待服务器确认。 2. SYN-ACK:服务器收到SYN包后,回应一个SYN-ACK(SYN+ACKnowledgment)包,告诉客户端其接收到了请求,并同意建立连接,此时服务器进入SYN_RECV状态。 3. ACK(确认字符):客户端收到服务器的SYN-ACK包后,发送一个ACK包给服务器,确认收到了服务器的确
193 1

热门文章

最新文章

相关实验场景

更多
下一篇
无影云桌面