2015-07-19 118 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