具有读/写的Java并发

时间:2014-04-26 11:52:25

标签: java concurrency client-server synchronized

我正在用Java创建一个客户端服务器应用程序。简而言之,Server有一些文件。客户端可以将文件发送到服务器,客户端可以请求从服务器下载所有文件。我正在使用RMI进行服务器和客户端通信,我正在使用RMI IO库在客户端和服务器之间发送文件。

一些示例代码:

服务器

class Server implements ServerService {

// private Map<String, File> files;
private ConcurrentHashMap<String, File> files // Solution

// adding a file to the server
public synchronized void addFile(RemoteInputStream inFile, String filename) 
    throws RemoteException, IOException {

    // From RMI IO library
    InputStream istream = RemoteInputStreamClient.wrap(inFile);
    File f = new File(dir, filename); 
    FileOutputStream ostream = new FileOutputStream(f);
    while (istream.available() > 0) {
        ostream.write(istream.read());
    }
    istream.close();
    ostream.close();
    files.put(filename, f);
}

// requesting all files
public requestFiles(ClientService stub) 
    throws RemoteException, IOException {

    for(File f: files.values()) {
        //Open a stream to this file and give it to the Client
        RemoteInputStreamServer istream = null; 
        istream = new SimpleRemoteInputStream(new BufferedInputStream(
                new FileInputStream(f)));
        stub.receiveFile(istream.export());
    }
}

请注意,这只是一些示例代码。

我的问题涉及对服务器上文件的并发访问。如您所见,我已经创建了addFile方法synchronized,因为它修改了我的服务器上的资源。我的requestFiles方法 synchronized

我想知道这是否会造成一些麻烦。当客户端A添加文件并且客户端B同时请求所有文件时,反之亦然,这会导致问题吗?或者addFile方法会等待(或让其他方法等待),因为它是synchronized

提前致谢!

1 个答案:

答案 0 :(得分:1)

是的,这可能会造成麻烦。其他线程可以访问requestFiles(),而单个线程正在执行addFile()方法。

  

两次调用同步方法是不可能的   在同一个对象上进行交错。当一个线程正在执行时   对象的synchronized方法,所有其他调用的线程   同一对象块的同步方法(暂停执行)   直到第一个线程完成对象。

[来源] http://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html

因此声明同步的方法会将实例锁定到该实例中的所有同步方法(在您的情况下是Server的实例)。如果您同时使用requestFiles()方法同步,那么您实际上将完全同步对Server实例的访问。所以你不会有这个问题。

您还可以在文件地图上使用同步块。请参阅此stackoverflow问题: Java synchronized block vs. Collections.synchronizedMap


话虽这么说,每当写入或读取文件时,基本上锁定整个Server对象的模型都会妨碍并发设计。

根据您的设计的其余部分,并假设您使用'addFile()'方法编写的每个文件具有不同的名称,并且您不会覆盖文件。我会探索以下内容:

完全删除地图,让每个方法分别与文件系统交互。

我会对'addFile()'编写的文件使用临时(.tmp)扩展名,然后(一旦写完文件)执行原子文件重命名,将扩展名转换为'.txt'文件

Files.move(src, dst, StandardCopyOption.ATOMIC_MOVE);

然后将整个'requestFiles()'方法限制为'.txt'文件。这样,文件写入和文件读取可以并行发生。

显然使用您需要的任何扩展程序。