需求: 提供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();
}
}