在前面的例子中,使用了guarded block和同步方法,在多个线程存取同共享对象FileArray时,防止出现线程间干扰。
(不熟悉guarded block和同步方法的同学,强烈建议先去看这个系列的前面几篇。)
实际上有更简便安全的方法,因为java提供了线程安全的并发集合对象(concurrent collections)。使用这些集合,可以自动实现线程安全,从而简化代码。
The java.util.concurrent
package includes a number ofadditions to the Java Collections Framework. These are most easilycategorized by the collection interfaces provided:
BlockingQueue 定义了一个先进先出的数据结构。可以在试图向一个已满队列中再加入对象,或试图从一个空队列中取出时,实现阻塞或超时。
在前面的例子中,我们使用了wait和notifyall方法来实现 guarded block。而使用BlockingQueue则会自动实现。
注意BlockingQueue只是一个接口,而实现了这个接口的类有:
ArrayBlockingQueue, DelayQueue, LinkedBlockingDeque, LinkedBlockingQueue, LinkedTransferQueue, PriorityBlockingQueue, SynchronousQueue
BlockingQueue接口定义的不同的方法对于现在无法满足,但将来可能满足的条件有不同的响应:
比如调用add()方法向一个已满queue中添加element时,会抛出异常。调用offer()则会返回false。调用put()则会被阻塞,直到有空间放入element时放入。
java api这部分介绍比较详细,值得一看。
ConcurrentMap是java.util.Map的子接口,它提供了有用的原子操作。可以实现在一个map中存在一个key时,删除或覆盖key-value pair,或一个key不存在时添加key-value pair.
The standard general-purpose implementation ofConcurrentMap
isConcurrentHashMap
, which is a concurrent analog ofHashMap
.想想在前面的例子中,为了防止scanner会重复向FileArray中存入同一个file,不得不使用重命名的方法:scanner将file放入FileArray时,会同时重命名文件为.upload。而scanner再次扫描目录时,会忽略.upload文件。而使用这个集合是不是就可以避免了呢?
实现了该接口的类是:ConcurrentHashMap, ConcurrentSkipListMap
ConcurrentNavigableMap 一个实现了模糊匹配的ConcurrentMap
.The standard general-purpose implementation ofConcurrentNavigableMap
isConcurrentSkipListMap
, which is a concurrent analog ofTreeMap
.
实现了该接口的类是:ConcurrentSkipListMap
接下来,就使用concurrent collections来改写前面的例子。
对于前面的FTP传输文件的例子,可以使用BlockingQueue,但在scanner将file放入queue时,仍然需要重命名来防止重复放入。
而使用ConcurrentMap,可以将文件名中的关键信息作为key,并在该key不存在时才向map中添加,从而防止scanner重复放入文件。
改代码去了~~~
貌似很容易遇到ConcurrentModificationException。。。