今天在修改java web后端代码时想到了多用户并发访问问题,想到了文件操作时存在安全问题,所以学习了一下文件锁的使用。利用java的FileLock类控制不同程序的并发访问,实现文件同步。
在java.nio包中提供了对文件的锁控制。主要是两个类,一个是FileChannel类,一个是FileLock类。FileLock类的作用是对FileChannel类进行实例化操作,而FileChannel类是对文件对象进行锁定。注意的是,文件锁对于一般的File类无效,想要使用文件锁需要以RandomAccessFile类创建文件对象。
注意点
1.锁类型:
共享锁: 共享读操作,但只能一个写(读可以同时,但写不能)。共享锁防止其他正在运行的程序获得重复的独占锁,但是允许他们获得重复的共享锁。只有一个读或一个写(读和写都不能同时)。独占锁防止其他程序获得任何类型的锁。
2.lock()和tryLock()的区别:
lock()阻塞的方法,锁定范围可以随着文件的增大而增加。无参lock()默认为独占锁;有参lock(0L, Long.MAX_VALUE, true)为共享锁。tryLock()非阻塞,当未获得锁时,返回null. 3.FileLock的生命周期:
在调用FileLock.release(),或者Channel.close(),或者JVM关闭
4.资源释放:
锁在被释放后才能被其他进程获得,通过release()方法释放,一般在用完文件后release()FileLock对象,close()FileChannel对象。文件锁的效果是与操作系统相关的,若想文件锁生效,则要保证其他程序也要按照API进行对文件访问。
代码使用
/**
* class文件上传保存,同时管理表达式数据库新增、更新功能
*
* @param file
* @param request
* @param response
* @throws IOException
* @throws JBOException
* @throws InterruptedException
*/
@RequestMapping("craw/customFunctionUpload.do")
public void fileUpload(@RequestParam(value = "classFile") MultipartFile file, HttpServletRequest request,
HttpServletResponse response) throws IOException, JBOException, InterruptedException {
if (file != null) {
String globalDir = request.getSession().getServletContext().getRealPath("/");
String fileName = file.getOriginalFilename();
String state = request.getParameter("state");
BizObjectManager bm = JBOFactory.getBizObjectManager("jbo.service.custom_function");
String version = "1";
int count = bm.createQuery("select count(*) from O where funcName=:funcName")
.setParameter("funcName", fileName.substring(0, fileName.length() - 6)).getSingleResult(false)
.getAttribute("count(*)").getInt();
// class文件新增上传
if ("0".equals(state)) {
// 数据库中已存在同名表达式
if (count >= 1) {
response.getWriter().print("{\"error\":\"已存在同名class!\"}");
return;
}
}
// class文件更新上传
else {
if (!fileName.substring(0, fileName.length() - 6).equals(state)) {
response.getWriter().print("{\"error\":\"上传的class文件名与要修改的表达式名不匹配!\"}");
return;
}
if (count >= 1) {
version = ""
+ (Integer
.valueOf(
bm.createQuery("select max(version) from O where funcName=:funcName")
.setParameter("funcName",
fileName.substring(0, fileName.length() - 6))
.getSingleResult(false).getAttribute("max(O.version)").getInt())
+ 1);
}
}
System.out.println(globalDir + "cfClasses/customfunction");
// 创建文件对象
File write = new File(globalDir + "cfClasses/files",
fileName.substring(0, fileName.length() - 6) + "-" + version + ".class");
if (write.getParentFile().exists() == false) {
System.out.println("dir not exist");
write.getParentFile().mkdirs();
}
write.createNewFile();
// 保存文件
// 对该文件加锁
RandomAccessFile out = new RandomAccessFile(write, "rw");
FileChannel fcout = out.getChannel();
FileLock flout = null;
while (true) {
try {
flout = fcout.tryLock();
break;
} catch (Exception e) {
System.out.println("有其他线程正在操作该文件,当前线程休眠1000毫秒");
Thread.sleep(1000);
}
}
InputStream ins = file.getInputStream();
// OutputStream ous = new FileOutputStream(write);
try {
byte[] buffer = new byte[1024];
ARE.getLog().debug("开始写文件:" + fileName);
int len = 0;
while ((len = ins.read(buffer)) > -1)
out.write(buffer, 0, len);
// ous.write(buffer, 0, len);
ARE.getLog().debug("已保存的文件:" + fileName);
} catch (Exception e) {
response.getWriter().print("{\"error\":\"文件保存失败\"}");
} finally {
ins.close();
}
//将存储文件临时拷贝到com.amarsoft.custom.function包下解析
File write2 = new File(globalDir + "cfClasses/com/amarsoft/custom/function", fileName);
if (write2.getParentFile().exists() == false) {
System.out.println("dir not exist");
write2.getParentFile().mkdirs();
}
write2.createNewFile();
//加锁
RandomAccessFile out2 = new RandomAccessFile(write2, "rw");
FileChannel fcout2 = out2.getChannel();
FileLock flout2 = null;
while (true) {
try {
flout2 = fcout2.tryLock();
break;
} catch (Exception e) {
System.out.println("有其他线程正在操作该文件,当前线程休眠1000毫秒");
Thread.sleep(1000);
}
}
ins = file.getInputStream();
try {
byte[] buffer = new byte[1024];
// ARE.getLog().debug("开始写文件:" + fileName);
int len = 0;
while ((len = ins.read(buffer)) > -1)
out2.write(buffer, 0, len);
// ARE.getLog().debug("已保存的文件:" + fileName);
} catch (Exception e) {
write.delete();
flout.release();
fcout.close();
out.close();
out = null;
response.getWriter().print("{\"error\":\"文件保存失败\"}");
return;
} finally {
ins.close();
flout2.release();
fcout2.close();
out2.close();
out2 = null;
flout.release();
fcout.close();
out.close();
out = null;
}
// 解析class文件,查看是否存在方法名为“action”的方法
String funcTemplateName = fileName.substring(0, fileName.length() - 6);
String funcTemplate = funcTemplateName + "(";
Map<String,Class> map = (Map<String, Class>) request.getSession().getServletContext().getAttribute("ClassMap");
synchronized(map){
//Class toDel = map.get(funcTemplateName);
FileClassLoader fileClsLoader = new FileClassLoader();
fileClsLoader.setClassPath(globalDir + "cfClasses");
// class文件中存在方法名为action的方法的标志
boolean flag = false;
int paramCount = 0;
String runFunc = "action";
Class cls = null;
try {
cls = fileClsLoader
.loadClass("com.amarsoft.custom.function." + fileName.substring(0, fileName.length() - 6));
Method[] mthds = cls.getMethods();
for (Method mthd : mthds) {
String methodName = mthd.getName();
if (methodName.equals(runFunc)) {
flag = true;
paramCount = mthd.getGenericParameterTypes().length;
for (int i = 1; i <= paramCount; i++) {
funcTemplate += "${" + i + "},";
}
if (paramCount > 0) {
funcTemplate = funcTemplate.substring(0, funcTemplate.length() - 1);
}
funcTemplate += ")";
}
}
} catch (Exception e) {
ARE.getLog().error("解析class文件模板函数失败!", e);
write.delete();
cls = null;
fileClsLoader = null;
response.getWriter().print("{\"error\":\"文件保存失败\"}");
} finally {
write2.delete();
System.out.println("write2 deleted!");
}
if (!flag) {
write.delete();
response.getWriter().print("{\"error\":\"class文件中不存在方法名为" + runFunc + "的方法\"}");
return;
}
if(cls != null) {
map.put(funcTemplateName, cls);
cls = null;
fileClsLoader = null;
}
}
BizObject obj = bm.newObject();
obj.setAttributeValue("funcName", fileName.substring(0, fileName.length() - 6));
obj.setAttributeValue("funcTemplate", funcTemplate);
obj.setAttributeValue("version", version);
obj.setAttributeValue("inputTime", StringFunction.getTodayNow());
obj.setAttributeValue("updateTime", StringFunction.getTodayNow());
bm.saveObject(obj);
response.getWriter().print("{}");
} else {
System.out.println("file empty");
response.getWriter().print("{\"error\":\"上传失败\"}");
}
}
参考文章
http://blog.csdn.net/wangbaochu/article/details/48546717 http://blog.csdn.net/gxy3509394/article/details/7435993