是否可以在Java ConcurrentLinkedQueue的元素上查看()

时间:2009-12-30 22:13:12

标签: java concurrency

我有一个程序,它有多个PublisherTask和SubscriberTask类型的对象。 给定订户可以订阅一个或多个发布者 为了描述我的问题,我将发布一些代码......

abstract class Publication {
    // some published information
}

class ConcretePublicationA extends Publication {

}

class ConcretePublicationB extends Publication {

}

abstract class Subscription {
    private final long id;
    private final Subscriber s;
    // PLUS some other members relating to the subscription

    protected Subscription(long id, Subscriber s){
        this.id = id;
        this.s =s;
    }

    public Subscriber getSubscriber() {
        return this.s;
    }


}

class ConcreteSubscriptionA extends Subscription {

    protected ConcreteSubscriptionA(long id, Subscriber s) {
        super(id, s);
        // TODO Auto-generated constructor stub
    }

}

class ConcreteSubscriptionB extends Subscription {

    protected ConcreteSubscriptionB(long id, Subscriber s) {
        super(id, s);
        // TODO Auto-generated constructor stub
    }

}

interface Subscriber {
    public void update(Publication pub);
}

interface Publisher {
    public Subscription subscribe(Subscriber subscriber);
}

abstract class PublisherTask implements Runnable, Publisher {
    private final ConcurrentHashMap<Long, Subscription> subscribers =
        new ConcurrentHashMap<Long, Subscription>();
    Long subscriptionId = 0L;

    @Override
    public void run() {
        /*obviously this is a different variable in a real program*/
        boolean some_condition = true;

        while(some_condition) {
            // do some work
            Publication pub = /* new ConcretePublication(....) */ null;

            for (Subscription s : subscribers.values()) {
                s.getSubscriber().update(pub);
            }
        }

    }

    @Override
    public Subscription subscribe(Subscriber subscriber) {
        Subscription sub;

        synchronized(subscriptionId) {
            /* the lines below are in a function in the sub-class,
             *  but for brevity I'm showing them here
             */
            sub = new ConcreteSubscriptionA(++subscriptionId, subscriber);
                    subscribers.put(subscriptionId, sub);
        }
        return sub ;
    }

}


abstract class SubscriberTask implements Runnable, Subscriber {

    protected ConcurrentLinkedQueue<Publication> newPublications =
        new ConcurrentLinkedQueue<Publication>();

    @Override
    public void run() {
        /*obviously this is a different variable in a real program*/
        boolean some_condition = true;

        while(some_condition) {
            // do some work
            Publication pub = newPublications.peek();

        /* the lines below are in a function in the sub-class,
         *  but for brevity I'm showing them here
         */
        {
            if (pub instanceof ConcretePublicationA) {
                // Do something with the published data
            } else if (pub instanceof ConcretePublicationB) {
                // Do something with the published data
            }
        }
        }
    }

    @Override
    public void update(Publication pub) {

        /* My question relates to this method:
             * Bascially to avoid memory issues I would like existing
             * unprocessed publications **Of Tth Same Type As The New One**
             * to be discarded
             */
        Publication existing = null;

        do {
            //This won't work coz peek() only looks at the head of the queue
                    existing = newPublications.peek();

            if ((existing != null) && (existing.getClass().equals(pub))) {
                newPublications.remove(existing);
            }
        } while (existing != null);
        newPublications.add(pub);
    }

好的,现在您已经有机会仔细检查我的代码了。我想问下列问题:
在上面显示的更新方法中,是否可以查看ConcurrentLinkedQueue中的所有元素并删除给定类型的元素?
另外,如果您认为可以对课程进行改进以及如何相互交流,请随时告诉我们 感谢

2 个答案:

答案 0 :(得分:1)

是的,可以使用迭代器查看ConcurrentLinkedQueue的所有元素。

Iterator<Publication> itr = newPublications.iterator();
while (itr.hasNext()) {
   Publication existing = itr.next();
   if (existing.getClass().equals(pub)) {
      itr.remove();
   }
}

因为ConcurrentLinkedQueue返回的迭代器保证遍历构造迭代器时存在的元素,并且可能(但不保证)反映构造之后的任何修改,您可能希望从外部锁定:

newPublications

总的来说,虽然这看起来效率不高,但应该调查完全修复重复出版物的替代解决方案。

答案 1 :(得分:0)

之前我已经解决了这个问题(或者类似的问题)。我解决它的方法是在每个新的Publication对象中有效地嵌入一个计数器(对于你正在做的事情,你需要一个每类类型的计数器)。如果消耗对象中的计数器小于当前值,则从队列中读取发布对象,然后丢弃,因为队列中有较晚的计数器。

但是,你必须小心这一点,你不会饿死你的cpu出版物,因为如果你继续快速添加新的出版物对象,你将永远不会实际处理它们中的任何一个,因为它们都将被标记为陈旧(我知道我的添加速度非常慢,比如每10分钟一次,我的任务每次运行需要几分钟,如果新的任务排在后面,那么旧的任务就没用了。)

你的解决方案看起来如果你添加A1,A2,B1,A3,A4,B2然后当你添加B2时你将不会删除B1,因为A1位于队列的头部并且不是同一类型,这不是看起来像你的评论所暗示的那样。

你可以通过输入一些帮助代码来实现我的想法:

class ValidCounter  {
    private Map<Class, AtomicLong> counters = new ConcurrentHashMap<Class, AtomicLong>();
    public int getAtomicLongFor(Class clz) {
        AtomicLong ans = counters.get(clz);
        if ( ans == null ) {
            counters.put(clz, new AtomicLong(0));
            return 0;
        }
        return ans.get();
    }
}

要在添加时使用,您可以在构造函数

中使用
long myValidCounter = validCounterInstance.getAtomicLongFor(getClass()).incrementAndGet();

要在检查是否仍应处理时使用

long currValidCounter = validCounterInstance.getAtomicLongFor(getClass()).get();
if ( pub.getValidCounter() >= currValidCounter ) {
    // still valid so process
} else {
    // superceeded, so ignore
}

请记住饥饿问题 - 你可能想要添加一些东西,这些东西关心你丢弃了多少或多长时间,而且只是拿了旧东西。

我还建议您不要使用peek,只使用民意调查,或者如果您有多个消费者,您很可能会遇到线程问题(多次处理的项目)。