欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

使用多线程往同一个文件写入数据的线程安全的例子(java实现)

程序员文章站 2024-03-21 20:12:40
...

本文给出了一个如何利用java提供的类MappedByteBuffer对文件进行并发写入的例子。具体实现思路就举个列子说明吧: 假设有10000个字节需要写入某个文件,为了加快写入速度,可以开启2个线程,第一个线程将前5000个字节写入文件0~4999的位置,另外一个线程将后5000个字节写入文件5000~9999的位置,等2个线程都写入完成后。我们就成功的将10000个字节完整的写入到了文件中。

值得一提的是,线程在写入过程中是会对自己所占用的那个文件区域枷锁的。各个线程加锁的区域互不重叠,否则会报OverlappingFileLockException异常

这个例子读者可以直接运行main方法查看效果,该类无任何外部依赖,全是jdk自带api。待写入的内容用源码中CONTENS字段表示,该字段记录了欧阳修名作《秋声赋》的完整内容。程序运行结束后可以到D盘查找名为ConcurrentWrite.txt的文件查看写入结果(D://ConcurrentWrite.txt),当然了,前提是你的电脑存在D盘。

程序不是很复杂,直接贴源码吧,关键地方有注释。另外,目标文件ConcurrentWrite.txt在程序运行期间不能被其他外部程序打开,否则写入失败!!!!

package com.lhever.modules.io.test;


import java.io.*;
import java.lang.reflect.Method;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.OverlappingFileLockException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

public class MappedByteBufferTest {

    //该字符串的内容将会被多线程写入到文件中
    static String CONTENS = "【作者】欧阳修 【朝代】宋 译文对照\n" +
            "欧阳子方夜读书,闻有声自西南来者,悚然而听之,曰:“异哉!”初淅沥以萧飒,忽奔腾而砰湃,如波涛夜惊,风雨骤至。其触于物也,鏦鏦铮铮,金铁皆鸣;又如赴敌之兵,衔枚疾走,不闻号令,但闻人马之行声。予谓童子:“此何声也?汝出视之。”童子曰:“星月皎洁,明河在天,四无人声,声在树间。”\n" +
            "\n" +
            "予曰:“噫嘻悲哉!此秋声也,胡为而来哉?盖夫秋之为状也:其色惨淡,烟霏云敛;其容清明,天高日晶;其气栗冽,砭人肌骨;其意萧条,山川寂寥。故其为声也,凄凄切切,呼号愤发。丰草绿缛而争茂,佳木葱茏而可悦;草拂之而色变,木遭之而叶脱。其所以摧败零落者,乃其一气之余烈。夫秋,刑官也,于时为阴;又兵象也,于行用金,是谓天地之义气,常以肃杀而为心。天之于物,春生秋实,故其在乐也,商声主西方之音,夷则为七月之律。商,伤也,物既老而悲伤;夷,戮也,物过盛而当杀。”\n" +
            "\n" +
            "“嗟乎!草木无情,有时飘零。人为动物,惟物之灵;百忧感其心,万事劳其形;有动于中,必摇其精。而况思其力之所不及,忧其智之所不能;宜其渥然丹者为槁木,黟然黑者为星星。奈何以非金石之质,欲与草木而争荣?念谁为之戕贼,亦何恨乎秋声!”\n" +
            "\n" +
            "童子莫对,垂头而睡。但闻四壁虫声唧唧,如助予之叹息耶。";


    public static void await(CountDownLatch latch) {
        if (latch == null) {
            return;
        }

        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }



    public static void main(String... args) {

        File file = getByName("D://ConcurrentWrite.txt");
        byte[] bytes = null;
        try {
            bytes = CONTENS.getBytes("UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        int len = bytes.length;
        System.out.println("len: " + len);
        int concurrentNum = 4;

        if (len < concurrentNum) {
            concurrentNum = 1;
        } else {
            concurrentNum = (len % concurrentNum == 0) ? concurrentNum : (concurrentNum + 1);
        }

        int size = len / concurrentNum;

        List<FileBlockWriter> writers = new ArrayList<FileBlockWriter>();

        CountDownLatch startLatch = new CountDownLatch(1);
        CountDownLatch endLatch = new CountDownLatch(concurrentNum);

        for (int i = 0; i < concurrentNum; i++) {
            FileBlockWriter writer = null;

            int from = i * size;
            int sliceLen = 0;
            System.out.println(from);

            if (i == concurrentNum - 1) {
                sliceLen = bytes.length - i * size;
                System.out.println(sliceLen);
            } else {
                sliceLen = size;
            }

            byte[] slice = new byte[sliceLen];
            System.arraycopy(bytes, from, slice, 0, sliceLen);
            writer = new FileBlockWriter(file, from, sliceLen, slice, startLatch, endLatch);

            writers.add(writer);
        }

        for (FileBlockWriter writer : writers) {
            writer.start();
        }

        //调用countDown后,所有写入线程才真正开始写入工作
        startLatch.countDown();

        //等待所有线程写入完成
        await(endLatch);

        System.out.println("done !!! ");


    }


    private static File getByName(String path) {

        File file = new File(path);

        if (!file.exists()) {
            try {
                file.createNewFile();
            } catch (IOException e) {
                throw new IllegalArgumentException("create file failed", e);
            }
        }

        if (file.isDirectory()) {
            throw new IllegalArgumentException("not a file");
        }

        return file;
    }


    /**
     * 该线程类负责将指定的内容(contents)写入文件(target),
     * 写入的起始位置是:from,往后写的字节数目是:length
     */
    public static class FileBlockWriter extends Thread {

        private File target;
        private int from;
        private int length;
        private byte[] contents;
        private CountDownLatch start;
        private CountDownLatch end;

        public FileBlockWriter(File target, int from, int length, byte[] contents, CountDownLatch start, CountDownLatch end) {
            this.target = target;
            this.from = from;
            this.length = length;
            this.contents = contents;
            this.start = start;
            this.end = end;
        }

        @Override
        public void run() {

            RandomAccessFile randFile = null;
            FileChannel channel = null;
            MappedByteBuffer mbb = null;
            FileLock fileLock = null;

            MappedByteBufferTest.await(start);

            try {

                randFile = new RandomAccessFile(target, "rw");
                channel = randFile.getChannel();
                mbb = channel.map(FileChannel.MapMode.READ_WRITE, from, length);

                fileLock = channel.lock(from, length, true);

                while (fileLock == null || !fileLock.isValid()) {
                    fileLock = channel.lock(from, length, true);
                    System.out.print("锁无效,重复获取");
                }
                mbb.put(contents);
                mbb.force();

            } catch (IOException e) {
                e.printStackTrace();
            } catch (OverlappingFileLockException e) {
                e.printStackTrace();
                throw new IllegalArgumentException("程序设计不合理,加锁区域相互重叠");

            } catch (Exception e) {
                e.printStackTrace();
            } finally {

                release(fileLock);
                forceClose(mbb);
                close(channel, randFile);
            }

            end.countDown();
        }
    }


    private static void release(FileLock fileLock) {
        if (fileLock == null) {
            return;
        }

        try {
            fileLock.release();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void close(Closeable... closeables) {


        if (closeables == null || closeables.length == 0) {
            return;
        }

        for (Closeable closeable : closeables) {
            if (closeable == null) {
                continue;
            }
            try {
                closeable.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 强制关闭MappedByteBuffer
     * @param mbb
     */
    private static void forceClose(MappedByteBuffer mbb) {
        AccessController.doPrivileged(new PrivilegedAction() {
            public Object run() {
                try {
                    Method getCleanerMethod = mbb.getClass().getMethod("cleaner", new Class[0]);
                    getCleanerMethod.setAccessible(true);
                    sun.misc.Cleaner cleaner = (sun.misc.Cleaner)
                            getCleanerMethod.invoke(mbb, new Object[0]);
                    cleaner.clean();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }
        });
    }


}
879675643@qq.com  lhever

.---.                                                                         
|   |   .              __.....__   .----.     .----.   __.....__              
|   | .'|          .-''         '.  \    \   /    /.-''         '.            
|   |<  |         /     .-''"'-.  `. '   '. /'   //     .-''"'-.  `. .-,.--.  
|   | | |        /     /________\   \|    |'    //     /________\   \|  .-. | 
|   | | | .'''-. |                  ||    ||    ||                  || |  | | 
|   | | |/.'''. \\    .-------------''.   `'   .'\    .-------------'| |  | | 
|   | |  /    | | \    '-.____...---. \        /  \    '-.____...---.| |  '-  
|   | | |     | |  `.             .'   \      /    `.             .' | |      
'---' | |     | |    `''-...... -'      '----'       `''-...... -'   | |      
      | '.    | '.                                                   |_|      
      '---'   '---'