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

 

目录
打赏
0
0
0
0
54
分享
相关文章
多进程同步之文件锁
【10月更文挑战第16天】文件锁是一种常用的多进程同步机制,它可以用于确保多个进程在访问共享资源时的互斥性。在使用文件锁时,需要注意锁的粒度、释放、竞争和性能等问题。通过合理使用文件锁,可以提高多进程程序的正确性和性能
进程间通信:文件锁
int flock(int fd, int operation); 其中,参数 fd 表示文件描述符;参数 operation 指定要进行的锁操作,该参数的取值有如下几种:LOCK_SH, LOCK_EX, LOCK_UN 和 LOCK_MAND LOCK_SH:表示要创建一个共享锁,...
1121 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,
1325 0
【YashanDB 知识库】如何避免 yasdb 进程被 Linux OOM Killer 杀掉
本文来自YashanDB官网,探讨Linux系统中OOM Killer对数据库服务器的影响及解决方法。当内存接近耗尽时,OOM Killer会杀死占用最多内存的进程,这可能导致数据库主进程被误杀。为避免此问题,可采取两种方法:一是在OS层面关闭OOM Killer,通过修改`/etc/sysctl.conf`文件并重启生效;二是豁免数据库进程,由数据库实例用户借助`sudo`权限调整`oom_score_adj`值。这些措施有助于保护数据库进程免受系统内存管理机制的影响。
|
1月前
|
Linux 进程前台后台切换与作业控制
进程前台/后台切换及作业控制简介: 在 Shell 中,启动的程序默认为前台进程,会占用终端直到执行完毕。例如,执行 `./shella.sh` 时,终端会被占用。为避免不便,可将命令放到后台运行,如 `./shella.sh &`,此时终端命令行立即返回,可继续输入其他命令。 常用作业控制命令: - `fg %1`:将后台作业切换到前台。 - `Ctrl + Z`:暂停前台作业并放到后台。 - `bg %1`:让暂停的后台作业继续执行。 - `kill %1`:终止后台作业。 优先级调整:
97 5
探索Linux中的`ps`命令:进程监控与分析的利器
探索Linux中的`ps`命令:进程监控与分析的利器
198 13
掌握taskset:优化你的Linux进程,提升系统性能
在多核处理器成为现代计算标准的今天,运维人员和性能调优人员面临着如何有效利用这些处理能力的挑战。优化进程运行的位置不仅可以提高性能,还能更好地管理和分配系统资源。 其中,taskset命令是一个强大的工具,它允许管理员将进程绑定到特定的CPU核心,减少上下文切换的开销,从而提升整体效率。
掌握taskset:优化你的Linux进程,提升系统性能
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
271 4
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
探索进程调度:Linux内核中的完全公平调度器
【8月更文挑战第2天】在操作系统的心脏——内核中,进程调度算法扮演着至关重要的角色。本文将深入探讨Linux内核中的完全公平调度器(Completely Fair Scheduler, CFS),一个旨在提供公平时间分配给所有进程的调度器。我们将通过代码示例,理解CFS如何管理运行队列、选择下一个运行进程以及如何对实时负载进行响应。文章将揭示CFS的设计哲学,并展示其如何在现代多任务计算环境中实现高效的资源分配。
【Linux】冯诺依曼体系结构与操作系统及其进程
【Linux】冯诺依曼体系结构与操作系统及其进程
239 1

相关实验场景

更多
下一篇
oss创建bucket
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等