高并发文件读写

目前着手的项目,是关于一个用文件做缓存的。具体过程是页面的每个请求都会触发一个线程来读取资源文件,当文件系统里不存在该文件时,则会从数据库中读取并编译出资源对象,然后序列化并保存到文件中。这样下次访问该页面时直接从文件系统中读取即可,这样达到了加速页面访问的效果。

这样的设计没什么问题,但实际实现中会有多线程同时读取同一个文件,并且某些现成读取该文件的时候其他线程可能正在写这个文件。所以这里需要进行文件同步。这里就需要对单个文件进行锁定,所以研究了一下文件锁FileLock.

说到FileLock,就必须理解nioNIO是最新一代的IO模型,是BIO的升级。NIO最主要的特点是

BIO是基于stream的IO操作,一次操作只能读或写一个byte,这样是非常容易创建数据的filter,以及filter Chain,但是缺点是速度慢。NIO是基于block的IO操作,一次操作可以读或者写一个数据block,速度自然就快了。NIO还有个更重要的特点就是将原本BIO中需要进行用户空间和内科空间进行buffer拷贝操作完全编程由系统内核间的拷贝,这对性能有非常大的提升。当然随着java自身的优化,原先BIO的一些基于stream的api也改成基于block的实现,从而优化了部分BIOapi的运行速度。JavaNIO中还提供了非常有用的功能,比如Memory-mapped file IO以及本文的重点File Lock等。

文件锁实际上和普通的java对象锁类似,都是advisory锁,主要用来在一个系统的不同部分对同一个文件的读写操作进行协作。通过文件锁可以锁住整个文件,也可以锁住文件的某一部分。如果获得了独占锁,那么系统的其他地方就不能获取这个文件或者这个文件被锁住的部分。如果获取的是共享锁,那么系统其他地方仍可以获取到这个锁,并访问这个文件。通过使用文件锁,就可以达到是文件的写操作原子化,同时不会对系统的其他部分造成干扰。

下面分享下如何使用文件锁解决项目中文件同时读写的问题。

  1. 写线程中需要获取独占锁。
  2. 读线程中不需要做任何特殊的处理。
@Slf4j
public class WriteThread implements Runnable {

    File file;
    int writeTime;
    WriteThread(String fileName, int writeTime) {
        this.file = FileUtils.getFile(fileName);
        this.writeTime = writeTime;
    }

    @Override
    public void run() {
        String currentThreadName = Thread.currentThread().getName();
        try {
            FileChannel fileChannel = new RandomAccessFile(file, "rw").getChannel();
            FileLock fileLock = null;
            while(true) {
                try {
                    fileLock = fileChannel.tryLock();
                    break;
                } catch (Exception e) {
                    Thread.sleep(100);
                }
            }
            log.error("start write thread {}", currentThreadName);
            fileChannel.write(ByteBuffer.wrap(("=======" + currentThreadName + "========").getBytes()));
            Thread.sleep(writeTime);
            log.error("end write thread {}", currentThreadName);

            fileLock.release();
            fileChannel.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
@Slf4j
public class ReadThread implements Runnable {
    File file;

    ReadThread(String fileName) {
        file = FileUtils.getFile(fileName);
   }

    @Override
    public void run() {
        String currentThreadName = Thread.currentThread().getName();
        try {
            FileInputStream fileInputStream = new FileInputStream(file);

            log.error("start read thread name: {}", currentThreadName);
            byte[] buffer = new byte[256];
            StringBuilder fileContent = new StringBuilder();
            int readLen = -1;
            while ((readLen = fileInputStream.read(buffer)) != -1) {
                fileContent.append(new String(buffer, 0, readLen));
            }
            log.info("read thread {} get file content: {}", currentThreadName, fileContent.toString());
            log.error("end read thread name: {}", currentThreadName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
@SpringBootApplication
@Slf4j
public class DemoApplication implements ApplicationRunner {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Override
    public void run(ApplicationArguments applicationArguments) throws Exception {
        String fileName = "test.test";
        Files.deleteIfExists(FileUtils.getFile(fileName).toPath());
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        executorService.execute(new WriteThread(fileName, 1000));
        executorService.execute(new WriteThread(fileName, 3000));
        Thread.sleep(50);
        // read when the first writing
        executorService.execute(new ReadThread(fileName));
        executorService.execute(new ReadThread(fileName));
        Thread.sleep(2000);
        // read when the send writing
        executorService.execute(new ReadThread(fileName));
        executorService.execute(new ReadThread(fileName));
        executorService.execute(new ReadThread(fileName));
        executorService.execute(new ReadThread(fileName));
        executorService.shutdown();
    }
}

运行结果如下:

start write thread pool-1-thread-1
start read thread name: pool-1-thread-3
read thread pool-1-thread-3 get file content: =======pool-1-thread-1========
end read thread name: pool-1-thread-3
start read thread name: pool-1-thread-4
read thread pool-1-thread-4 get file content: =======pool-1-thread-1========
end read thread name: pool-1-thread-4
end write thread pool-1-thread-1
start write thread pool-1-thread-2
start read thread name: pool-1-thread-5
start read thread name: pool-1-thread-6
start read thread name: pool-1-thread-8
start read thread name: pool-1-thread-7
read thread pool-1-thread-7 get file content: =======pool-1-thread-2========
end read thread name: pool-1-thread-7
read thread pool-1-thread-5 get file content: =======pool-1-thread-2========
read thread pool-1-thread-6 get file content: =======pool-1-thread-2========
read thread pool-1-thread-8 get file content: =======pool-1-thread-2========
end read thread name: pool-1-thread-5
end read thread name: pool-1-thread-6
end read thread name: pool-1-thread-8
end write thread pool-1-thread-2

结果非常理想,可以完美解决问题。赞一个

原文地址:http://liangcloud.cn/file-lock/

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容

  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,169评论 11 349
  • Java SE 基础: 封装、继承、多态 封装: 概念:就是把对象的属性和操作(或服务)结合为一个独立的整体,并尽...
    Jayden_Cao阅读 2,095评论 0 8
  • 1、Netty基础入门 Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应...
    我是嘻哈大哥阅读 4,686评论 0 31
  • NIO(Non-blocking I/O,在Java领域,也称为New I/O),是一种同步非阻塞的I/O模型,也...
    闪电是只猫阅读 3,077评论 0 7
  • 我发怒了,这是不对的 不知道为什么,今天整个人的状态都非常差,以至于让我的好伙伴都觉得我似乎要离职了。果然已经烂到...
    百界无疆KOKO阅读 170评论 0 0