问题描述
我正在创建一个多线程服务器,并且创建了一个用于管理用户的类,但是我注意到,当一个线程正在读取文件而另一个线程正在写入文件时,我的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());
}
}
}
1楼
您可以将UserManager
所有方法声明为synchronized
。
这将防止方法被多个线程同时执行。
但是,更好的解决方案是使用 ,其中load
不ReadLock
使用ReadLock
,而save
操作使用WriteLock
。
读锁可以由多个线程同时获取,而写锁则授予对一个线程的独占访问权限。
2楼
您需要查找synchronized
关键字。
如果声明必须以下列方式专门执行的所有方法:
public static synchronized User loadUser(String id, Socket s) {
// ^^^^^^^^^^^^
它们只能在该类的线程之间相互排斥地执行。
通常,人们倾向于使synchronized
方法成为非静态方法,但是如果您确定一个应用程序永远不会有两个用户管理器,则此方法很好。
听起来很合理。