@Override
  public ChannelGroupFuture close(ChannelMatcher matcher) {
    if (matcher == null) {
      throw new NullPointerException("matcher");
    }

    Map<Channel, ChannelFuture> futures = new LinkedHashMap<Channel, ChannelFuture>(size());

    if (stayClosed) {
      // It is important to set the closed to true, before closing channels.
      // Our invariants are:
      // closed=true happens-before ChannelGroup.close()
      // ChannelGroup.add() happens-before checking closed==true
      //
      // See https://github.com/netty/netty/issues/4020
      closed = true;
    }

    for (Channel c : serverChannels) {
      if (matcher.matches(c)) {
        futures.put(c, c.close());
      }
    }
    for (Channel c : nonServerChannels) {
      if (matcher.matches(c)) {
        futures.put(c, c.close());
      }
    }

    return new DefaultChannelGroupFuture(this, futures, executor);
  }
  @Override
  public boolean add(Channel channel) {
    ConcurrentSet<Channel> set =
        channel instanceof ServerChannel ? serverChannels : nonServerChannels;

    boolean added = set.add(channel);
    if (added) {
      channel.closeFuture().addListener(remover);
    }

    if (stayClosed && closed) {

      // First add channel, than check if closed.
      // Seems inefficient at first, but this way a volatile
      // gives us enough synchronization to be thread-safe.
      //
      // If true: Close right away.
      // (Might be closed a second time by ChannelGroup.close(), but this is ok)
      //
      // If false: Channel will definitely be closed by the ChannelGroup.
      // (Because closed=true always happens-before ChannelGroup.close())
      //
      // See https://github.com/netty/netty/issues/4020
      channel.close();
    }

    return added;
  }
 @Override
 public ChannelGroup flush(ChannelMatcher matcher) {
   for (Channel c : nonServerChannels) {
     if (matcher.matches(c)) {
       c.flush();
     }
   }
   return this;
 }
  /**
   * Returns the {@link ChannelGroupFuture} which will be notified when all {@link Channel}s that
   * are part of this {@link ChannelGroup}, at the time of calling, are closed.
   */
  public ChannelGroupFuture newCloseFuture(ChannelMatcher matcher) {
    Map<Channel, ChannelFuture> futures = new LinkedHashMap<Channel, ChannelFuture>(size());

    for (Channel c : serverChannels) {
      if (matcher.matches(c)) {
        futures.put(c, c.closeFuture());
      }
    }
    for (Channel c : nonServerChannels) {
      if (matcher.matches(c)) {
        futures.put(c, c.closeFuture());
      }
    }

    return new DefaultChannelGroupFuture(this, futures, executor);
  }
  @Override
  public ChannelGroupFuture writeAndFlush(Object message, ChannelMatcher matcher) {
    if (message == null) {
      throw new NullPointerException("message");
    }

    Map<Channel, ChannelFuture> futures = new LinkedHashMap<Channel, ChannelFuture>(size());

    for (Channel c : nonServerChannels) {
      if (matcher.matches(c)) {
        futures.put(c, c.writeAndFlush(safeDuplicate(message)));
      }
    }

    ReferenceCountUtil.release(message);

    return new DefaultChannelGroupFuture(this, futures, executor);
  }
  @Override
  public boolean remove(Object o) {
    if (!(o instanceof Channel)) {
      return false;
    }
    boolean removed;
    Channel c = (Channel) o;
    if (c instanceof ServerChannel) {
      removed = serverChannels.remove(c);
    } else {
      removed = nonServerChannels.remove(c);
    }
    if (!removed) {
      return false;
    }

    c.closeFuture().removeListener(remover);
    return true;
  }
  @Override
  public ChannelGroupFuture deregister(ChannelMatcher matcher) {
    if (matcher == null) {
      throw new NullPointerException("matcher");
    }

    Map<Channel, ChannelFuture> futures = new LinkedHashMap<Channel, ChannelFuture>(size());

    for (Channel c : serverChannels) {
      if (matcher.matches(c)) {
        futures.put(c, c.deregister());
      }
    }
    for (Channel c : nonServerChannels) {
      if (matcher.matches(c)) {
        futures.put(c, c.deregister());
      }
    }

    return new DefaultChannelGroupFuture(this, futures, executor);
  }