2014-08-28 36 views
5

stworzyłem małą aplet Java na prosty cel: Kiedy to się nazywa, to wykonaj następujące kroki:Jak zsynchronizować dostęp do plików w serwlecie Java?

  1. odczytać pliku foo.json z lokalnego systemu plików
  2. przetwarzać dane z pliku i zrobić kilka zmian do niego
  3. Napisz powrotem zmiany w pliku

uproszczona wersja kodu:

@Override 
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 
    FileInputStream inputStream = new FileInputStream("foo.json"); 
    String filecontent = IOUtils.toString(inputStream); 
    inputStream.close(); 

    JSONObject json = new JSONObject(filecontent); 

    doSomeChangesTo(json); 

    FileWriter writer = new FileWriter("foo.json"); 
    writer.write(json.toJSONString()); 
    writer.flush(); 
    writer.close(); 
} 

Teraz mam do czynienia z problemem, że może się zdarzyć, że aplet zostanie wywołany w tym samym czasie przez dwa lub więcej żądań http do serwletu. Aby uniknąć wielokrotnego równoległego zapisu w tym samym pliku, muszę to jakoś zsynchronizować. Z mojej wiedzy na temat procesu cyklu serwletu, każde żądanie tworzy nowy wątek, więc użycie FileLock prawdopodobnie nie miałoby żadnego wpływu:

Blokady plików są przechowywane w imieniu całej wirtualnej maszyny Java. One nie są odpowiednie do kontrolowania dostępu do pliku przez wiele wątków w obrębie tej samej maszyny wirtualnej.

(Od http://docs.oracle.com/javase/7/docs/api/java/nio/channels/FileLock.html)

Chyba, że ​​za pomocą słowa kluczowego synchronized(){} nie będzie również działać, ponieważ chcę, aby zsynchronizować dostęp do systemu plików, a nie dostęp do zmiennych/obiektów.

Jak można zsynchronizować dostęp do systemu plików w moim aplecie, gdy ma miejsce wiele równoległych żądań dla tego serwletu?

+0

Zmień nazwę pliku podczas pracy nad nią ?Sądzę, że używanie DB zamiast pliku nie jest decyzją, którą należy podjąć? – Fildor

+0

Jeśli twoja aplikacja jest jedyną, która zapisuje do pliku (i nie jest w klastrze), synchronizacja działałaby dobrze. Ludzie zwykle korzystają z bazy danych, która obsługuje dla ciebie równoczesny dostęp. –

+0

Co dokładnie * chcesz tu zsynchronizować? Czy jest to cała procedura odczytu-procesu-zapisu? Czy jest to osoba czytająca i zapisująca, podczas gdy przetwarzanie może się przeplatać (I możesz zapisać dane ze starego odczytu)? – Ordous

Odpowiedz

6

myślę, że za pomocą słowa kluczowego synchronized(){} nie będzie również działać, ponieważ chcę, aby zsynchronizować dostęp do systemu plików, a nie dostęp do zmiennych/obiektów.

Korzystanie z synchronized może działać. Zakładasz, że jeśli chcesz kontrolować dostęp do obiektu X z wielu wątków, musisz użyć do tego obiektu obiektu synchronized. Ty nie. Można użyć obiektu synchronized na obiekcie dowolnym obiekcie, pod warunkiem, że wszystkie dostępy korzystają z tego samego obiektu.

W rzeczywistości często lepiej używać oddzielnego obiektu blokady private do synchronizacji, ponieważ wtedy niemożliwe jest synchronizowanie kodu poza klasą na zamku.

Więc może masz coś takiego, z czego jeden przykład dla każdego udostępnionego pliku:

public class SharedFile 
{ 
     private final File path; 
     private final Object lock = new Object(); 

     public SharedFile(File path) { 
     this.path = path; 
     } 

     public void process(.....) throws IOException { 
     synchronized(lock) { 
      try(InputStream = new FileInputStream(path)) { 
       .... 
      } 
     } 
     } 
} 
+0

Nie, obiekt blokady jest tutaj ważny – mavroprovato

3

można użyć Semaphore, co następuje:

private static Semaphore semaphore = new Semaphore(1); 

public void doSomeChangesTo(JSONObject json) { 
    try { 
     semaphore.acquire(); 

     // doSomeChangesTo 

    } finally { 
     semaphore.release(); 
    } 
} 
+0

Dzięki za edycję @ bcsb1001 – troig