2015-07-19 61 views
0

我正在创建一个多线程服务器,并且我创建了一个类来管理用户,但我注意到当一个线程正在读取文件时,我的UserManager类可能会导致错误,并且其他的是写入文件,如何防止它?Java多线程阻止两个函数同时运行

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

如果您使用Java 7+,为什么使用'File'?使用java.nio.file代替 – fge

回答

2

你可以只申报UserManager所有的方法为​​。这会阻止方法同时由多个线程执行。

更好的解决方案,但是,这是使用ReadWriteLock其中load oparations使用ReadLocksave操作使用WriteLock。读锁可以同时由多个线程获取,而写锁授予对单个线程的独占访问权限。

+0

谢谢,但我想声明所有方法同步只会阻止它从多个线程同时读取或写入,而不是从一个线程尝试写入和其他尝试同时读取? – Ladas125

+1

@ Ladas125只要一个线程正在执行其中一个线程,同步这些方法将阻止其他线程进入它们。因此,同步您的示例类中的方法将起作用。如果你想允许多个读者线程(即调用'load *'方法的线程)但在'save'操作期间阻塞任何线程,你应该考虑一个'ReadWriteLock'。 –

1

您需要查找​​关键字。

如果声明都必须完全成为被执行的方法:

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

他们只能与该类线程之间的互斥执行。 通常人们更喜欢使​​方法非静态,但如果您确定应用程序永远不会有两个用户管理器,则此方法没有问题。这听起来很合理。

+0

谢谢,但我想宣布所有方法同步只会阻止它从多个线程同时读取或写入,而不是从一个线程试图写入和其他尝试同时读取? – Ladas125

+1

@ ladas125该类上的所有静态方法是互斥的。一个线程不能在另一个阅读时写入。正如Stefan Ferstl所说,如果你想要许多读者,但是独家编写,你可以使用ReadWriteLock,但是我怀疑这是在一个小应用程序中的工程。它肯定只会有助于如果频繁的并发读取。 – Persixty