狂奔 lion

自强不息

2010年7月7日

浅谈Java中的同步的方法和原理

Java的内存模型中Thread会附有自己的堆栈,寄存器,必要时需要和主存即heap之间同步。
可以使用Synchornized关键字和Concurrent包中的Lock可以保证线程互斥和可见性。

互斥性体现在类锁或者对象锁上,每个对象自身都包含一个监视器,该监视器是一个每次只能被一个线程所获取进入的临界区,可以通过wait和notify来退出和准入临界区。可以看出这是一个生产者-消费者的模型。而Concurrent包中的Lock为了能够获得更好的性能和更好的扩展性,以及不依赖于关键字的可读代码,自己实现了这样一个生产消费队列,也就是AbstractQueuedSynchronizer,被称为AQS的机制。每个Lock都内置了一个AbstractQueuedSynchronizer。需要说明的是AbstractQueuedSynchronizer内部实现采用了CAS机制,通过getState, setState, compareAndSetState访问控制一个32bit int的形式进行互斥。

那么可见性是如何保证的呢?

对于关键字的同步机制,其实可见性就是线程和主存之间的同步时机问题。共有4个时间点需要注意:
1 获取或释放类锁/对象锁的时候。Thread保证reload/flush全部变更
2 volatile就是flush on write或者reload on read
3 当线程首次访问共享变量时,可以得到最新的结果。
题外:所以在构造方法中公布this时很危险的。简单的说,就是构造时不逃脱任何变量,不开启新的线程,只做封装。关于安全构造,请参考
http://www.ibm.com/developerworks/cn/java/j-jtp0618/#resources
4 线程结束时,所有变更会写回主存

关于Concurrent Lock如何实现可见性的问题,Doug Lea大侠,只在他的论文中提到,按照JSR133,Unsafe在getState, setState, compareAndSetState时保证了线程的变量的可见性,不需要额外的volatile支持,至于具体这些native做了哪些magic就不得而知了,总之,最后的contract就是保证lock区间的共享变量可见性。开发团队被逼急了就这样回答:
There seems to be a real reluctance to explain the dirty details. I think the question was definitely understood on the concurrent interest thread, and the answer is that synchronized and concurrent locking are intended to be interchangable in terms of memory semantics when implemented correctly. The answer to matfud's question seems to be "trust us.”

不过这个地方的确是开发团队给我们用户迷惑的地方,在同样应用了CAS机制的Atomic类中,都内嵌了volatile变量,但是再lock块中,他告诉我们可以保证可见性。

感兴趣的同学可以下面的两个thread和Doug Lea的thesis:
http://altair.cs.oswego.edu/pipermail/concurrency-interest/2005-June/001587.html
http://forums.sun.com/thread.jspa?threadID=631014&start=15&tstart=0
http://gee.cs.oswego.edu/dl/papers/aqs.pdf

posted @ 2010-07-09 19:49 杨一 阅读(1822) | 评论 (0)编辑 收藏

commons-net FTPClient API存取设计

文件系统无非就是文件的存取和组织结构。
访问一个文件系统的API也应该是写,读,定位方法(Pathname?/URI?)

FTPClient针对文件的保存和获取各提供了两个方法,分别是:

public boolean storeFile(String remote, InputStream local)
public OutputStream storeFileStream(String remote)

public boolean retrieveFile(String remote, OutputStream local)
public InputStream retrieveFileStream(String remote)

 

两个方法貌似相同,实际不同,返回流的那个因为不能马上处理流,所以需要用户手工调用completePendingCommand,而另一个传递流进去的则不需要。可能有同学已经遇到过这个问题了,读写第一个文件时总是正确的,当相同API读写第二个文件时,block住了。这是因为FTPClient要求在进行流操作之后执行completePendingCommand,以确保流处理完毕,因为流处理不是即时的,所以也没有办法不手工调用completePendingCommand。问题是开发者把不返回流的方法末尾加上了completePendingCommand,如果不看代码可能根本不知道。
文档上说:

     * There are a few FTPClient methods that do not complete the
     
* entire sequence of FTP commands to complete a transaction.  These
     
* commands require some action by the programmer after the reception
     
* of a positive intermediate command.  After the programmer's code
     * completes its actions, it must call this method to receive
     
* the completion reply from the server and verify the success of the
     
* entire transaction.


但是这样仍然还是让人有点困惑,为什么都是存储/读取的方法,有时候要调用completePendingCommand,有时候不调用?更严重的问题是completePendingCommand调用了getReply,如果一个命令通过socket stream传了过去但是没有getReply,即没有completePendingCommand,那么下次发命令时,将会受到本次返回码的干扰,得到无效的响应。而如果在completePendingCommand之后又进行了一次无辜的completePendingCommand,那么因为FTP Server上没有Reply了,就会block。所以completePendingCommand并不是可以随意添加的。

现在出现了两个问题:
1 completePendingCommand很容易多出来或遗漏
2 显式调用completePendingCommand暴露了底层实现,给用户带来不便,用户只想要InputStream或者OutputStream

为了解决这个问题,可以对InputStream进行扩展,建立一个ReplyOnCloseInputStream,如下:

private static ReplyOnCloseInputStream extends InputStream{
  
//
  public ReplyOnCloseInputStream(InputStream is, FTPClient c){
    
//
  }

  
//
  @override
  
public void close(){
    
if(c.completePendingCommand){
      is.close();
    }
else{
      
//throw Exception
    }

  }

}
 
//
return new ReplyOnCloseInputStream(is, client);


这样封装之后,FTPClient的用户只需要正常在处理完流之后关闭即可,而不必暴露实现细节。保存文件也可以用相同的方法封装OutputStream。

posted @ 2010-07-07 23:08 杨一 阅读(3390) | 评论 (1)编辑 收藏

<2010年7月>
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567

导航

公告

本人在blogjava上发表的文章及随笔除特别声明外均为原创或翻译,作品受知识产权法保护并被授权遵从 知识分享协议:署名-非商业性使用-相同方式共享 欢迎转载,请在转载时注明作者姓名(杨一)及出处(www.blogjava.net/yangyi)
/////////////////////////////////////////
我的访问者

常用链接

留言簿(5)

随笔分类(55)

随笔档案(55)

相册

Java

其他技术

生活

最新随笔

搜索

积分与排名

最新评论

阅读排行榜

评论排行榜

自强不息


用心 - 珍惜时间,勇于创造