【问题标题】:round robin scheduling java iterators循环调度java迭代器
【发布时间】:2011-01-03 17:52:50
【问题描述】:

我有一个数组中的主机列表,它代表可用于执行特定工作的服务器。目前我只是遍历列表查找并与主机建立通信以检查其不忙。如果没有,我将向它发送工作。这种方法往往意味着列表中的第一个主机往往会持续变热,而负载未与其余可用主机正确平衡。

在伪代码中..

for (Host h : hosts) {

    //checkstatus
    if status == job accepted break;

}

我想在主机之间适当地平衡这种负载,即第一次使用主机一,第二次使用该方法主机 2。只是想知道最优雅的解决方案是??

谢谢 W

【问题讨论】:

  • 我想可能是我之后。可能为我的主机数组列表实现了一个自定义迭代器。只是不确定如何做到这一点。我感觉我需要扩展 ArrayList 类并实现 ListIterator 类,这听起来合理吗?

标签: java scheduling iterator


【解决方案1】:

提供的实现是错误的,在并行的情况下可能会失败,我最简单的方法是使用循环链表,其指针由原子整数维护。

【讨论】:

    【解决方案2】:

    您可以创建一种提供循环迭代的新型 Iterable:

    public class RoundRobin<T> implements Iterable<T> {
          private List<T> coll;
    
          public RoundRobin(List<T> coll) { this.coll = coll; }
    
          public Iterator<T> iterator() { 
             return new Iterator<T>() {
                private int index = 0;
    
                @Override
                public boolean hasNext() {
                    return true;
                }
    
                @Override
                public T next() {
                    T res = coll.get(index);
                    index = (index + 1) % coll.size();
                    return res;
                }
    
                @Override
                public void remove() {
                    throw new UnsupportedOperationException();
                }
    
            };
        }
    }
    

    您需要将您的主机定义为RoundRobin&lt;Host&gt;

    [根据 Mirko 的评论修复]

    【讨论】:

    • 很好的解决方案。不过有一个小问题:执行 coll.get(index) 对于某些列表来说是昂贵的。我会说维护一个迭代器并在它上面做下一步,当你用完元素时获得一个新的迭代器会更好。
    • 你的实现是错误的,因为 next() 的第一次调用提供了一个空值!见:stackoverflow.com/a/12931655/1268954
    【解决方案3】:
        public class RoundRobinIterator<T> implements Serializable {
    
            private static final long serialVersionUID = -2472203060894189676L;
            //
            private List<T> list;
            private Iterator<T> it;
            private AtomicInteger index = new AtomicInteger(0);
    
            public RoundRobinIterator(List<T> list) throws NullPointerException {
                super();
                if (list==null) {
                    throw new NullPointerException("List is null");
                }
                this.list=Collections.unmodifiableList(list);
            }
            public RoundRobinIterator(Collection<T> values) {
                this(new ArrayList<T>(values));
            }
            public RoundRobinIterator(Iterator<T> values) {
                this(copyIterator(values));
            }
            public RoundRobinIterator(Enumeration<T> values) {
                this(Collections.list(values));
            }
    
    
    
            private final List<T> getList() {
                return list;
            }
            private final Iterator<T> getIt() {
                return it;
            }
            public final int size() {
                return list.size();
            }
            public final synchronized T getNext(Filter<T> filter) {
                int start = index.get();
                T t = getNext();
                T result = null;
                while ((result==null) && (start!=getIndex())) {
                    if (filter.accept(t)) {
                        result=t;
                    } else {
                        t = getNext();
                    }
                }
                return result;
            }
    
            public final synchronized T getNext() {
                if (getIt()==null) {
                    if (getList().size()==0) {
                        index.set(0);
                        return null;
                    } else {
                        it = getList().iterator();
                        index.set(0);
                        return it.next();
                    }
                } else if (it.hasNext()) {
                    index.incrementAndGet();
                    return it.next();
                } else {
                    if (list.size()==0) {
                        index.set(0);
                        return null;
                    } else {
                        index.set(0);
                        it = list.iterator();               
                        return it.next();
                    }
                } 
            }
    
            public final synchronized int getIndex() {
                return index.get();
            }
    
    
            private static <T> List<T> copyIterator(Iterator<T> iter) {
                List<T> copy = new ArrayList<T>();
                while (iter.hasNext()) {
                    copy.add(iter.next());
                }
                return copy;
            }
        }
    

    过滤器在哪里

        public interface Filter<T> {
    
            public boolean accept(T t);
    
        }
    

    【讨论】:

      【解决方案4】:

      如果您正在创建一个迭代器,最好先创建一个防御性副本,然后让迭代器处理它。

      return new MyIterator(ImmutableList.<T>copyOf(list));
      

      【讨论】:

      • of 应该是copyOf;否则,您有一个列表,其中包含对原始列表的引用作为其唯一元素。
      • 是的。对不起,那里的错字很糟糕
      【解决方案5】:

      我的RoundRobin实现,基于https://stackoverflow.com/a/2041772/1268954的实现

      /**
       * 
       * @author Mirko Schulze
       *
       * @param <T>
       */
      public class RoundRobin<T> implements Iterable<T> {
      
          private final List<T>   coll;
      
          public RoundRobin(final List<T> coll) {
              this.coll = NullCheck.throwExceptionIfNull(coll, "collection is null");
          }
      
          @Override
          public Iterator<T> iterator() {
              return new Iterator<T>() {
      
                  private int index;
      
                  @Override
                  public boolean hasNext() {
                      return true;
                  }
      
                  @Override
                  public T next() {
                      this.index = this.index % RoundRobin.this.coll.size();
                      final T t = RoundRobin.this.coll.get(this.index);
                      this.index++;
                      return t;
                  }
      
                  @Override
                  public void remove() {
                      throw new IllegalArgumentException("remove not allowd");
                  }
              };
          }
      }
      

      还有 Junit 测试用例

      /**
       * 
       * @author Mirko Schulze
       *
       */
      @RunWith(JUnit4.class)
      public class RoundRobinTest extends TestCase {
      
          private List<Integer> getCollection() {
              final List<Integer> retval = new Vector<Integer>();
              retval.add(Integer.valueOf(1));
              retval.add(Integer.valueOf(2));
              retval.add(Integer.valueOf(3));
              retval.add(Integer.valueOf(4));
              retval.add(Integer.valueOf(5));
              return retval;
          }
      
          @Test
          public void testIteration() {
              final List<Integer> l = this.getCollection();
              final Integer frst = l.get(0);
              final Integer scnd = l.get(1);
              final Integer thrd = l.get(2);
              final Integer frth = l.get(3);
              final Integer last = l.get(4);
              Assert.assertEquals("die Collection hat für diesen Test nicht die passende Größe!", 5, l.size());
              final RoundRobin<Integer> rr = new RoundRobin<Integer>(l);
              final Iterator<Integer> i = rr.iterator();
              for (int collectionIterations = 0; collectionIterations < 4; collectionIterations++) {
                  final Integer i1 = i.next();
                  Assert.assertEquals("nicht das erste Element", frst, i1);
                  final Integer i2 = i.next();
                  Assert.assertEquals("nicht das zweite Element", scnd, i2);
                  final Integer i3 = i.next();
                  Assert.assertEquals("nicht das dritte Element", thrd, i3);
                  final Integer i4 = i.next();
                  Assert.assertEquals("nicht das vierte Element", frth, i4);
                  final Integer i5 = i.next();
                  Assert.assertEquals("nicht das letzte Element", last, i5);
              }
          }
      }
      

      【讨论】:

        【解决方案6】:

        恕我直言,标准 Java API 已经提供了一种简单的方法来实现这一点,而无需借助外部库,甚至无需实现自定义迭代器。只需使用 Deque 即可拉出第一个服务器,使用或丢弃它,然后将其附加回 Deque 的末尾。下面是一些示例代码:

        // Initialize the Deque. This might be at your class constructor. 
        Deque<Host> dq = new ArrayDeque<Host>();
        dq.addAll(Arrays.asList(hosts)); 
        
        void sendJob(Job myJob) {
            boolean jobInProcess = false;
            do {
                Host host = dq.removeFirst(); // Remove the host from the top
                if(!host.isBusy()) {
                    host.sendJob(myJob);
                    jobInProcess = true;
                }
                dq.addLast(host); // Put the host back at the end
            } 
            while(!jobInProcess); // Might add another condition to prevent an infinite loop...    
        }
        

        这只是一个示例,您始终在循环中按循环顺序 ping 主机,仅当其中一个可用并接手工作时才结束。您可以轻松地修改它,只在队列中转一次(使用最大设置为队列大小的计数器)或在抛出异常之前多次,或者在轮次之间休眠以避免在所有人都忙时撞到主机.

        【讨论】:

          【解决方案7】:

          Google collections 有一个实用方法 Iterators.cycle(Iterable&lt;T&gt; iterable) 可以满足您的需求。

          【讨论】:

            【解决方案8】:

            如果列表是可变的,并且与使用主机的 I/O 相比,编辑它的成本可以忽略不计,则可以旋转它:

            List<String> list = Arrays.asList("one", "two", "three");
            Collections.rotate(list, -1);
            System.out.println(list);
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-03-02
              • 2011-10-24
              • 2012-07-20
              • 2017-09-08
              相关资源
              最近更新 更多