在
线程与任务文中,虽然创建了多线程,并且线程之间出现了一些不可预测的CPU调度,但是由于线程之间是相互隔离的——线程没有访问共同的资源,尽管在执行任务的过程可能被CPU剥夺运行权,但是当它们再次获得运行权时对运行结果并没有影响,它们是安全的。
实际上,上篇文章通过join()
方法演示了
一种安全访问共享资源的方法。
考虑一种情况,如果多个线程访问同一资源,并对资源内容进行修改,会发生什么情况?
对于非原子性操作,多线程下会出现竞争条件。例如,对于操作accounts[to] += amount
,可以被拆分为多个CPU指令:
- 加载accounts[to]到寄存器
- 增加amount
- 将结果写回acounts[to]
上述3个步骤中,线程执行到任一步骤时都可能被剥夺运行权。
如此一来,最后的结果就变得不可预测。
...Queue(队列),实际开发过程中,在单线程环境下使用的并不多,Queue作为集合框架中重要组成似乎习惯性被忽略。队列总是先持有元素,再处理元素。

Queue继承关系简图
...Java语言设计总是按值调用
按值调用:方法接收的是调用者提供的值
按引用调用:方法接收的是调用者提供的变量地址
一个方法可以修改传递引用所对应的变量值,但是不能修改传递值所对应的变量值。
...
可重入锁
#
Java SE 5之后提供了位于java.util.concurrent.locks
包下的显式互斥机制——Lock对象(显式锁),Lock对象必须被显式的创建,锁定和释放。
一般情况下 ,ReentrantLock保护代码块的基本结构是:
1myLock.lock(); // 可重入锁
2try{
3 // 临界区代码
4}finally{
5 myLock.unlock();
6}
这个结构可以确保同一时间只有一个线程进入临界区( critical section ),其他线程调用lock()
时会被阻塞,直到第一个线程释放锁。
...Set
是不含重复元素的集,严格来讲,Set
不允许当e1.equals(e2)
为真时, e1 和 e2 同时出现在集合中。Set
最多允许一个null
元素。
将可变对象置入Set
时需要特别小心,当对象的改动影响到了元素之间的equals()
比较的结果,那么Set
的行为就变得不确定了。因此,不能将Set本身作为Set的元素。
...不同的使用环境下,final
关键字的含义有细微差别,但通常它指“这是无法改变的”。
...
自Java 1.0开始,每一个对象都有一个隐式内部锁( intrinsic lock ),在Java API Specification中通常被称为监视器( monitor )。这个内部锁由synchronized
关键字提供支持。synchronized
关键字的语义就是“同步的”,这意味着使用这个关键字可以处理共享资源的冲突。
当访问被synchronized
关键字保护的方法或代码块时,它将检查锁能否获得——这个锁可以是当前类对象的锁,也可以是一个临时锁( ad-hoc lock ),取决你如何使用,任务执行完成之后会释放锁。
和ReentrantLock
一样,synchronized
关键字获取的锁也是独占锁,并且也是“可重入”的,某个任务可以多次获得对象的锁,并由计数器维护获得锁的次数,当退出一个方法时,计数器-1,完全退出时,才释放锁,这和可重入锁的机制是一样的。
...由于Map
的键是Set
,因此使用可变对象作为Map
的key时,需要覆盖 equals 和 hashCode 方法,Map不能使用自身作为key。
Java 8对Map
接口进行了优化,新增了主要是针对函数式接口的 默认 方法(方法体被省略):
default V merge (K key, V value,
BiFunction<? super V, ? super V, ? extends V> remappingFunction) {...}
default V compute (K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {...}
default V computeIfPresent (K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {...}
default V computeIfAbsent (K key,
Function<? super K, ? extends V> mappingFunction) {...}
default V replace (K key, V value) {...}
default boolean replace(K key, V oldValue, V newValue) {...}
default boolean remove (Object key, Object value) {...}
default V putIfAbsent (K key, V value) {...}
default void replaceAll (
BiFunction<? super K, ? super V, ? extends V> function) {...}
default V getOrDefault (Object key, V defaultValue) {...}
上述方法使用的不多,主要用来对Map键值进行更新,按需查阅API文档。
...