并发访问文件代码

需求: 提供32个文件池给多个线程写日志使用,当某个文件大小达到一定的size或者文件打开时间达到预设值,需要把32个文件同时移动另外一个目录做进一步的处理,再创建32个新文件继续提供服务。

没怎么写过多线程代码,纠结了几天,写了以下的代码,测试ok,以后再慢慢研究是否有更优解,


1. 使用synch而没有使用lock是因为在jdk6上面synch的性能得到了很大的优化,赞个jdk。

2. 不建议使用代码里面的logger.warning (e.getMessage())方式,应该使用logger.log(Level.WARNING,e.getMessage(),e),因为前者可能会导致exception信息丢失,有时候excpetion里面的message可能为空。

3. 不建议使用singleton模式了,没办法mock,除非为类建立了interface,使用DI或者factory mode


import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.util.LinkedList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Logger;

import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
import javax.swing.Box.Filler;

/**
 * 
 * To maintain lists to monitor available and locked files for external usage
 * 
 */
@ThreadSafe
public class FileListMaintainer {

    private static final Logger logger = Logger
            .getLogger(FileListMaintainer.class.getName());

    /**
     * List for primary maintaining available files
     */

    @GuardedBy("this")
    private List<File> itsPrimaryAvailableList = new LinkedList<File>();

    /**
     * List for primary maintaining locked files
     */
    @GuardedBy("this")
    private List<File> itsPrimaryLockedList = new LinkedList<File>();

    /**
     * List for secondary maintaining available files
     */
    @GuardedBy("this")
    private List<File> itsSecondaryAvailableList = new LinkedList<File>();

    /**
     * List for secondary maintaining locked files
     */
    @GuardedBy("this")
    private List<File> itsSecondaryLockedList = new LinkedList<File>();

    /**
     * list of current used avaialbeList
     */
    @GuardedBy("this")
    private List<File> currentAvailableList = null;
    /**
     * list of current used lockedList
     */
    @GuardedBy("this")
    private List<File> currentLockedList = null;

    @GuardedBy("this")
    private boolean isPrimaryListInUse = false;

    @GuardedBy("this")
    private boolean isProcessingInProgress = false;

    @GuardedBy("this")
    private int moveFileCount = 0;

    @GuardedBy("this")
    private long moveTimes = 0;

    /**
     * Instance for FileListMaintainer
     */
    private static FileListMaintainer listInstance = null;

    // private static final int KILOBYTES = 1024;

    private static String tempDirectlyPath = "temp";

    private static String finalDirectlyPathString = "final";

    private static String tempFileNameString = "InputProcessing";

    private static final int fileNumber = 32;

    private static final long maxFileSize = 1024 * 100;

    /**
     * Constructor
     * 
     * @throws IOException
     */
    private FileListMaintainer() throws IOException {

        File file = new File(tempDirectlyPath);
        file.deleteOnExit();
        // System.out.println(file.getAbsolutePath());
        file.mkdir();
        /*
         * if(!file.mkdir()) throw new
         * IOException("fail to create temp folder");
         */
        file = new File(finalDirectlyPathString);
        file.deleteOnExit();
        /*
         * if(!file.mkdir()) throw new
         * IOException("fail to create final folder");
         */
        file.mkdir();

        String itsTempName = null;
        StringBuilder buffer = new StringBuilder(tempDirectlyPath);
        buffer.append(System.getProperty("file.separator"));
        // buffer.append(InetAddress.getLocalHost().getHostName());
        buffer.append(tempFileNameString);

        itsTempName = buffer.toString();

        /*
         * clear the list if any unwanted(deleted) files are left in the list
         * before creating the new files
         */
        itsPrimaryAvailableList.clear();
        itsSecondaryAvailableList.clear();

        /*
         * creation of processed temp files: Processed_Temp.0 -
         * Processed_Temp.63
         */
        for (int fName = 0; fName < fileNumber; fName++) {

            StringBuilder appendedName = new StringBuilder(itsTempName);
            appendedName.append('.');
            appendedName.append(fName);

            File tmpFile = new File(appendedName.toString());
            tmpFile.deleteOnExit();
            // System.out.println(tmpFile.getAbsolutePath());
            tmpFile.createNewFile();
            itsPrimaryAvailableList.add(tmpFile);

            appendedName = new StringBuilder(itsTempName);
            appendedName.append('.');
            appendedName.append(fName + fileNumber);

            tmpFile = new File(appendedName.toString());
            tmpFile.deleteOnExit();
            tmpFile.createNewFile();
            itsSecondaryAvailableList.add(tmpFile);
        }

        currentAvailableList = itsPrimaryAvailableList;
        currentLockedList = itsPrimaryLockedList;
        isPrimaryListInUse = true;
       // startTimer();
    }

    static {

        try {
            listInstance = new FileListMaintainer();
        } catch (IOException e) {
            logger.warning(e.getMessage());
            throw new RuntimeException(e);
        }
    }

    /**
     * Singleton implementation of the Class
     * 
     * @return sole instance of the ListMaintainer
     * @throws Throwable
     */
    public static FileListMaintainer getInstance() {
        assert (null != listInstance);
        return listInstance;
    }

    /**
     * first try to get an available file, if fails, sleep 0.01 second then
     * retry, loop until success
     * 
     * @return available file from current available list
     */

    public File getAvailableFile() {

        File file = getAvailableFileInternal();

        while (null == file) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                logger.finer(e.getMessage());
            }
            file = getAvailableFileInternal();
        }
        return file;
    }

    /**
     * get available file from current file list,
     * 
     * @return if file is not null, return the file if file is null, return
     *         null, if file is not null but size reach the max size, trigger
     *         file processing, release the file and return null
     */
    private File getAvailableFileInternal() {

        File file = getAvailableFileFromCurrentList();
        if (null != file && file.length() >= maxFileSize) {
           // stopTimer();
            startFileProcessing();
            releaseLockOnFile(file);
            file = null;
        }
        return file;
    }

    /**
     * Remove file from the top of the available list and then move the same to
     * Locked list
     * 
     * @return the next available file from the Available List
     */
    @GuardedBy("this")
    public synchronized File getAvailableFileFromCurrentList() {
        /*
         * if (null == listInstance || itsAvailableList.isEmpty()){ try {
         * listInstance = getInstance(); } catch (Throwable e) {
         * logger.warning(e.getMessage()); } }
         */

        if (!currentAvailableList.isEmpty()) {
            File nxtFile = currentAvailableList.remove(0);
            currentLockedList.add(nxtFile);
            return nxtFile;
        }

        return null;
    }

    /**
     * Release the acquired lock on the file and put it back into the available
     * list
     * 
     * @param fileRel
     *            specifies the file whose lock is to removed
     * 
     */
    @GuardedBy("this")
    public synchronized void releaseLockOnFile(File aRelFile) {

        if (!isProcessingInProgress) {
            releaseLockOnFileInternal(currentAvailableList, currentLockedList,
                    aRelFile);
        } else {
            /*
             * in moving process, release file may belongs to primary or
             * secondary
             */
            boolean moved = false;
            if (itsSecondaryLockedList.contains(aRelFile)) {
                moved = releaseLockOnFileInternal(itsSecondaryAvailableList,
                        itsSecondaryLockedList, aRelFile);
                if (isPrimaryListInUse && moved)
                    moveFileCount++;
            } else if (itsPrimaryLockedList.contains(aRelFile)) {
                moved = releaseLockOnFileInternal(itsPrimaryAvailableList,
                        itsPrimaryLockedList, aRelFile);
                if (!isPrimaryListInUse && moved)
                    moveFileCount++;
            } else {
                logger.warning("unknow file, file path: "
                        + aRelFile.getAbsolutePath());
            }

            if (fileNumber == moveFileCount)
                countinueFileProcessing();
        }

    }

    private boolean releaseLockOnFileInternal(List<File> availableList,
            List<File> lockedList, File aRelFile) {
        Boolean isRel = lockedList.remove(aRelFile);
        if (!isRel) {
            /* if the lock was not successfuly released */
            logger.fine("Lock not released");
            return false;
        }
        availableList.add(aRelFile);
        return true;
    }

    /**
     * start swap file list and move the ready file list to final place
     * 
     * @return true if start successfully, false if already started and in
     *         processing
     */
    @GuardedBy("this")
    public synchronized boolean startFileProcessing() {

        if (isProcessingInProgress) {
            logger.warning("swapping in progress, ingore this request");
            return false;
        }

        isProcessingInProgress = true;

        moveTimes++;

        /* step 1: swap file ist */
        if (isPrimaryListInUse) {
            currentAvailableList = itsSecondaryAvailableList;
            currentLockedList = itsSecondaryLockedList;
            moveFileCount = itsPrimaryAvailableList.size();
            isPrimaryListInUse = false;
        } else {
            currentAvailableList = itsPrimaryAvailableList;
            currentLockedList = itsPrimaryLockedList;
            moveFileCount = itsSecondaryAvailableList.size();
            isPrimaryListInUse = true;
        }

        return true;
    }

    /**
     * Check whether file movement is in progressing
     * 
     * @return true if in progressing and false if not
     */
    @GuardedBy("this")
    public synchronized boolean isFileProcessingInProgress() {
        return isProcessingInProgress;
    }

    @GuardedBy("this")
    private synchronized boolean countinueFileProcessing() {
        /*
         * get all available files in previous used list move files to Tmpsort,
         * create new empty files identify the swap is finished.
         */

        /* step 3: finish the processing */
        if (isPrimaryListInUse) {
            if (fileNumber != itsSecondaryAvailableList.size()) {
                logger.warning("not in the right status to continue, isPrimaryListInUse: "
                        + isPrimaryListInUse
                        + "move list size: "
                        + itsSecondaryAvailableList.size());
                return false;
            }
            /*
             * assert(fileNumber == itsSecondaryAvailableList .size());
             */
            moveAndCreateNewFile(itsSecondaryAvailableList);
        } else {
            if (fileNumber != itsPrimaryAvailableList.size()) {
                logger.warning("not in the right status to continue, isPrimaryListInUse: "
                        + isPrimaryListInUse
                        + "move list size: "
                        + itsPrimaryAvailableList.size());
                return false;
            }
            /*
             * assert(fileNumber == itsPrimaryAvailableList .size());
             */
            moveAndCreateNewFile(itsPrimaryAvailableList);
        }

        finishFileProcessing();
        return true;
    }

    private void moveAndCreateNewFile(List<File> list) {
        for (File file : list) {

            String newFileName = file.getName();

            if (!file.renameTo(new File(finalDirectlyPathString + "/"
                    + newFileName + "_" + moveTimes))) {
                logger.warning("fail to rename file: " + file.getAbsolutePath());
                // throw new Exception(file.getAbsolutePath());
            }

            try {
                file.createNewFile();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

    }

    private void finishFileProcessing() {
        isProcessingInProgress = false;
        //startTimer();
    }

    /*
    private static Timer itsTimer = null;

    private static boolean isTimerRunning = false;
    
    public static void startTimer() {
        if(isTimerRunning){
            if (null != itsTimer)
                itsTimer.cancel();
            return;
        }

        isTimerRunning = true;
        
        itsTimer = new Timer();

        int delay = 1000 * 20;

        itsTimer.scheduleAtFixedRate(new TimerTask() {

            @Override
            public void run() {
                FileListMaintainer.getInstance().startFileProcessing();
            }
        }, delay, delay);
    }

    public static synchronized void stopTimer() {
        if (null != itsTimer)
            itsTimer.cancel();
    }
    */

}

测试代码
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Timer;
import java.util.TimerTask;

public class Test2 {

    
    private static byte[] b = new byte[512];
    
    private static Timer itsTimer = null;
    
    static {
        //b[0] = 'A';
        for (int i=0; i<127; i++){
            b[i] = (byte)i;b[i+128] = (byte)i;
            b[i+256] = (byte)i;b[i+384] = (byte)i;
        }
    }
    
    /**
     * @param args
     */
    public static void main(String[] args) {
        
        for(int i=0; i < 32; i++){
            new Thread(new Runnable(){

                @Override
                public void run() {
                    
                    while(true){
                    
                    File file = FileListMaintainer.getInstance().getAvailableFile();
                    
                    RandomAccessFile raf;
                    try {
                        raf = new RandomAccessFile(file, "rw");
                        raf.seek(file.length());
                        
                        raf.write(b,0,400);
                        raf.close();
                    } catch (FileNotFoundException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    } finally {
                        
                        FileListMaintainer.getInstance().releaseLockOnFile(file);
                    }
                 }
                    
                }
                
            }).start();
        }
        
        startTimer();
       
       
    }

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值