Java

资源访问受限--引论

线程与任务文中,虽然创建了多线程,并且线程之间出现了一些不可预测的CPU调度,但是由于线程之间是相互隔离的——线程没有访问共同的资源,尽管在执行任务的过程可能被CPU剥夺运行权,但是当它们再次获得运行权时对运行结果并没有影响,它们是安全的。

实际上,上篇文章通过join()方法演示了 一种安全访问共享资源的方法

考虑一种情况,如果多个线程访问同一资源,并对资源内容进行修改,会发生什么情况?

对于非原子性操作,多线程下会出现竞争条件。例如,对于操作accounts[to] += amount,可以被拆分为多个CPU指令:

  1. 加载accounts[to]到寄存器
  2. 增加amount
  3. 将结果写回acounts[to]

上述3个步骤中,线程执行到任一步骤时都可能被剥夺运行权。

如此一来,最后的结果就变得不可预测。

...

Queue

Queue(队列),实际开发过程中,在单线程环境下使用的并不多,Queue作为集合框架中重要组成似乎习惯性被忽略。队列总是先持有元素,再处理元素1

J7NBrQ.png

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不含重复元素的集,严格来讲,Set不允许当e1.equals(e2)为真时, e1e2 同时出现在集合中。Set最多允许一个null元素。

可变对象置入Set时需要特别小心,当对象的改动影响到了元素之间的equals()比较的结果,那么Set的行为就变得不确定了。因此,不能将Set本身作为Set的元素

...

synchronized关键字

自Java 1.0开始,每一个对象都有一个隐式内部锁intrinsic lock ),在Java API Specification中通常被称为监视器monitor )。这个内部锁由synchronized关键字提供支持。synchronized关键字的语义就是“同步的”,这意味着使用这个关键字可以处理共享资源的冲突。

当访问被synchronized关键字保护的方法或代码块时,它将检查锁能否获得——这个锁可以是当前类对象的锁,也可以是一个临时锁( ad-hoc lock ),取决你如何使用,任务执行完成之后会释放锁。

ReentrantLock一样,synchronized关键字获取的锁也是独占锁,并且也是“可重入”的,某个任务可以多次获得对象的锁,并由计数器维护获得锁的次数,当退出一个方法时,计数器-1,完全退出时,才释放锁,这和可重入锁的机制是一样的。

...

HashMap和TreeMap

由于Map的键是Set,因此使用可变对象作为Map的key时,需要覆盖 equalshashCode 方法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文档。

...