The first code is unsafe - imagine that you are actually written:
HashMap<String, ConcurrentHashMap<String, String>> strongMap =
new HashMap<String, ConcurrentHashMap<String, String>>();
Map<String, ? extends AbstractMap<String, String>> map = strongMap;
Now:
map.put("one", new HashMap<String, String>());
ConcurrentHashMap<String, String> x = strongMap.get("one");
We must have ConcurrentHashMap, but in reality we have only HashMap.
Actually it’s much easier to explain if we reduce the number of generic files ... your script is really equivalent (say):
List<? extends Fruit> list = new List<Apple>();
list.add(new Apple());
which looks fine until you think it's equivalent to reality (as far as the compiler is concerned):
List<Apple> apples = new ArrayList<Apple>();
List<? extends Fruit> list = apples;
list.add(new Orange());
Apple apple = list.get(0);
, , . , .