1

使用synchronized塊,我有以下的代碼片段:在ExecutorService的

public class Service<T> { 
    private ConcurrentMap<Integer, Integer> locks = new ConcurrentHashMap<Integer, Integer>(); 
    public final ExecutorService exec = Executors.newFixedThreadPool(5); 

    public Future<Boolean> foo(T o, String fileName) throws IOException { 
     return exec.submit(new Callable<Boolean>() { 
      @Override 
      public Boolean call() throws IOException { 
       File f = new File(fileName); 

//    synchronized (getCacheSyncObject(name.hashCode())) { 
//     try (OutputStream out = new FileOutputStream(f)) { 
//      //some actions 
//     } 
//    } 

       try (OutputStream out = new FileOutputStream(f)) { 
        //some actions 
       } 

       return true; 
      } 
     }); 
    } 

    private Object getCacheSyncObject(final Integer id) { 
     locks.putIfAbsent(id, id); 
     return locks.get(id); 
    } 
} 

public class Main { 
    public static void main(String[] args) throws IOException { 
     Object obj = new Object(); 

     Service<Object> service = new Service<>(); 
     Future<Boolean> result1 = service.foo(obj, "filename"); // is there a deadlock? 
     service.exec.shutdown(); 
    } 
} 

我想完成一個簡單的任務。我需要一個獨佔的文件鎖定。爲了得到這個,我把文件的文件名鎖定到同步塊。但在這種情況下,我從foo方法中沒有得到任何東西。我的程序沒有結束。

+0

該程序將創建大約與它在其生命週期中讀取的不同文件數量一樣多的鎖。 (或者,如果它運行的時間很長,鎖的數量將漸近地接近2^32)這可能不是你想要的。如果我打算將文件映射到鎖,我將擁有一個較小的鎖池 - 可能會有一些小的倍數(2倍或3倍)的文件數量,我希望它們可以同時打開。 –

+0

您可以根據給定路徑名的散列值選擇一個鎖,但可能會有多個不同的路徑名引用同一個文件。 –

+0

你說過,「我需要獨佔文件鎖定。」大多數看到「文件鎖定」字樣的程序員會認爲你想防止兩個或多個_processes_同時訪問同一個文件。如果您僅嘗試同步兩個或多個_threads的活動,則可能希望避免說「文件鎖定」._ –

回答

1

如果您正在尋找服務範圍內的鎖,則可以使用其字段來存儲鎖。但我建議不要使用fileName.hashCode(),因爲可能的衝突。使用全名來代替:

import java.io.File; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.io.OutputStream; 
import java.util.concurrent.Callable; 
import java.util.concurrent.ConcurrentHashMap; 
import java.util.concurrent.ConcurrentMap; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.Future; 

class Service<T> { 

    private final ConcurrentMap<String, Object> locks = new ConcurrentHashMap<>(); 
    public final ExecutorService exec = Executors.newFixedThreadPool(5); 

    public Future<Boolean> foo(T o, 
           final String fileName) throws IOException { 
     return exec.submit(new Callable<Boolean>() { 
      @Override 
      public Boolean call() throws IOException { 
       File f = new File(fileName); 

       synchronized (getCacheSyncObject(fileName)) { 
        try (OutputStream out = new FileOutputStream(f)) { 
         out.write("hi".getBytes()); 
        } 
       } 

       return true; 
      } 
     }); 
    } 

    private Object getCacheSyncObject(final String name) { 
     Object result = locks.get(name); 

     if (result == null) { 
      result = new Object(); 
      Object prevLock; 
      if ((prevLock = locks.putIfAbsent(name, result)) != null) { 
       result = prevLock; 
      } 
     } 

     return result; 
    } 
} 

public class Test { 

    public static void main(String[] args) throws Exception { 
     Object obj = new Object(); 

     Service<Object> service = new Service<>(); 
     Future<Boolean> result1 = service.foo(obj, "filename"); 
     service.exec.shutdown(); 

     System.out.println("result1 = " + result1.get()); 
    } 
} 

順便說一句,即使你有hashCode()方法的碰撞,你可以在文件中意外的輸出,但如果你沒有鎖定任何事情儘量

try (OutputStream out = new FileOutputStream(f)) { 
    out.write("hi".getBytes()); 
} 

你不應該陷入僵局。我建議檢查你的「//一些動作」

+0

感謝您提供良好的答案。 – Denis