我正在创建一个多线程服务器并且我创建了一个用于管理用户的类,但是我注意到我的UserManager类可能在一个线程正在读取文件而另一个正在写入该文件时导致错误,如何防止它?
package server.questiongiver;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.util.ArrayList;
import server.engine.CustomLog;
public class UserManager {
public static User loadUser(String id, Socket s) {
User user = null;
if (id == null) {
return user;
}
File f = new File("users.dat");
if (f.isFile() && f.canRead()) {
try (BufferedReader br = new BufferedReader(new FileReader(f))) {
String line;
while ((line = br.readLine()) != null) {
if (line.equals("[" + id + "]")) {
user = new User(s);
user.id = id;
user.password = br.readLine().split("-separator-")[1];
user.username = br.readLine().split("-separator-")[1];
break;
}
}
}
catch (IOException ex) {
CustomLog.error(ex.getMessage());
}
}
return user;
}
public static ArrayList<User> loadAllUsers() {
File f = new File("users.dat");
ArrayList<User> users = new ArrayList();
if (f.isFile() && f.canRead()) {
try (BufferedReader br = new BufferedReader(new FileReader(f))) {
String line;
while ((line = br.readLine()) != null) {
if (line.matches("^(\\[[0-9]*\\])$")) {
User user = new User(null);
user.id = line.replace("[", "").replace("]", "");
user.password = br.readLine().split("-separator-")[1];
user.username = br.readLine().split("-separator-")[1];
users.add(user);
}
}
}
catch (IOException ex) {
CustomLog.error(ex.getMessage());
}
}
return users;
}
public static void saveUser(User user) {
File f = new File("users.dat");
String content = "";
String newLine = System.getProperty("line.separator");
boolean found = false;
if (f.isFile() && f.canRead()) {
try (BufferedReader br = new BufferedReader(new FileReader(f))) {
String line;
while ((line = br.readLine()) != null) {
if (line.equals("[" + user.id + "]") && br.readLine().equals(user.password)) {
found = true;
content += "[" + user.id + "]" + newLine;
content += "password-separator-" + user.password + newLine;
content += "username-separator-" + user.username + newLine;
br.readLine();
}
else {
content += line + newLine;
}
}
}
catch (IOException ex) {
CustomLog.error(ex.getMessage());
}
}
if (!found) {
content += "[" + user.id + "]" + newLine;
content += "password-separator-" + user.password + newLine;
content += "username-separator-" + user.username + newLine;
}
try (BufferedWriter writer = new BufferedWriter(new FileWriter(f))) {
writer.write(content);
writer.close();
}
catch (FileNotFoundException | UnsupportedEncodingException ex) {
CustomLog.error(ex.getMessage());
}
catch (IOException ex) {
CustomLog.error(ex.getMessage());
}
}
}
答案 0 :(得分:2)
您可以在UserManager
中将所有方法声明为synchronized
。这样可以防止多个线程同时执行这些方法。
然而,更好的解决方案是使用ReadWriteLock
load
操作使用ReadLock
,save
操作使用WriteLock
。读锁可以由多个线程同时获取,而写锁授予对单个线程的独占访问权。
答案 1 :(得分:1)
您需要查找synchronized
关键字。
如果您声明所有必须专门执行的方法:
public static synchronized User loadUser(String id, Socket s) {
// ^^^^^^^^^^^^
它们只能在该类的线程之间互斥时执行。
通常人们更喜欢使synchronized
方法非静态,但如果您确定应用程序永远不会有两个用户管理器,那么这种方法很好。这听起来很合理。