当前位置: 代码迷 >> java >> Java多线程阻止两个函数同时运行
  详细解决方案

Java多线程阻止两个函数同时运行

热度:22   发布时间:2023-07-31 12:03:48.0

我正在创建一个多线程服务器,并且创建了一个用于管理用户的类,但是我注意到,当一个线程正在读取文件而另一个线程正在写入文件时,我的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());
        }
    }
}

您可以将UserManager所有方法声明为synchronized 这将防止方法被多个线程同时执行。

但是,更好的解决方案是使用 ,其中loadReadLock使用ReadLock ,而save操作使用WriteLock 读锁可以由多个线程同时获取,而写锁则授予对一个线程的独占访问权限。

您需要查找synchronized关键字。

如果声明必须以下列方式专门执行的所有方法:

public static synchronized User loadUser(String id, Socket s) {
//            ^^^^^^^^^^^^

它们只能在该类的线程之间相互排斥地执行。 通常,人们倾向于使synchronized方法成为非静态方法,但是如果您确定一个应用程序永远不会有两个用户管理器,则此方法很好。 听起来很合理。

  相关解决方案