use CopyOnWriteArrayList to store listeners