【1月27日】并发(三):锁--/*新春快乐*/

来源:互联网 发布:青岛知豆 6000一年 编辑:程序博客网 时间:2024/05/17 04:59

通过线程安全对象来管理Servlet的全部状态,可以维护Servlet的线程安全性。如上一篇博客所提及。但是这样的限制是,只能在Servlet中加入一个状态变量。如果加入更多的状态变量,就可能引发潜在的安全问题。《java并发编程实践》针对前文中提到的完成因数分解的Servlet提出了一个新的需求:我们缓存最新的计算结果,以应对两个连续的客户请求相同的数字进行因数分解。要实现这个需求,需要同时缓存最新请求的数字和它的因数。书中的例子仅仅分别保证了lastNubmer和lastFactors的原子性,导致了竞争条件的出现。当一个不变约束涉及到多个变量时,变量间不是相互独立的:某个变量的值会制约其他几个变量的值。因此,更新一个变量的时候,要在同一个原子操作中更新其他几个。为了保护状态的一致性,要在单一的原子操作中更新相互关联的状态变量。

1.1      内部锁

Java提供了强制原子性的机制:synchronized块。一个synchronized块有两部分:锁对象的引用,以及这个锁保护的代码块。synchronized方法是对跨越整个方法体的synchronized块的简短描述。至于synchronized方法的锁,就是该方法所在的对象本身。

synchronized(lock){

//访问或修改被锁保护的共享状态

}

每个java对象都可以隐式地扮演一个用于同步的锁的角色;这些内置的锁被称为内部锁或者监视器锁。执行线程在进入synchronized块之前会自动获得锁;而无论通过正常控制路推出,还是从块中抛出异常,线程都在放弃对synchronized块控制的时候自动释放锁。获得内部锁的唯一途径是:进入这个内部锁保护的同步块或者方法。

内部锁在java中扮演者互斥锁(mutual exclusionlock)的角色,意味着至多只有一个线程可以拥有锁。当线程A尝试一个被线程B占用的锁时,线程A必须等待或者阻塞,直到B释放。

需要注意的是,尽管同步机制可以简化线程安全的操作,但也会带来一个非线性安全的问题—性能的问题。

1.2      重进入(Reentrancy)

当一个线程请求其他线程占用的锁时,请求线程会被阻塞。然而内部锁是可重进入的,因此线程在试图获得它自己占用的锁时,请求会成功。

这可能有点难理解,为什么一个已经占用锁的线程会再次请求占用锁呢,这在稍后会以例子说明啦(不得不说这些概念确实挺绕的,但是看书和看视频比较起来,看视频写demo会比较快上手,但是后劲不足;看书虽然抓耳挠腮,如品名茗,久乃觉脾沛生香)。重进入意味着所有的请求是基于“线程”而不是“调用”。重进入的实现是通过微每个锁关联一个请求计数(acquistion count)和一个占用它的线程。当计数为0时,认为锁是未被占用的。线程请求一个未被占用的锁时,JVM会记录锁的拥有者,并将计数请求置为1;如果同一个线程再次请求这个锁,计数将递增。每次占用的线程退出同步块,计数将递减。直到计数请求达到0时,锁就被释放。

重进入的机制对锁行为进行了封装,简化了面向对象并发代码的开发。Widget,子类覆写父类的方法,如果没有重进入的机制,就会死锁。

Widget.class

public class Widget {public synchronized void doSomething(){...}}public class LoggingWidget extends Widget{public synchronized void doSomething(){System.out.println(toString()+": calling doSomething");super.doSomething();}}


1.3      用锁保护状态

因为锁使得线程能够串行地访问锁保护的代码路径,所以我们可以用锁来创建相关的协议,以保证线程对共享状态的访问。

操作共享状态的复合状态必须是原子的,以避免竞争条件,比如读-改-写操作和检查再运行操作。复合操作会在完整的运行期间占用锁,以确保其行为是原子的。对于每个可被多个线程访问的可变状态变量,如果所有访问它的线程在执行时都占用同一个锁。则称这个变量是由这个变量保护的。

对象的内部锁和它的状态之间没有内在的关系。尽管大多数的类普遍使用这样的一种非常有效的机制:用对象的内部锁来保护所有的域,然而这并不是必需的。即使获得了对象关联的锁业不能阻止其他线程访问这个对象—获得对象的锁后,唯一可以做的是阻止其他线程获得相同的锁

锁保护的变量,意味着每一次访问变量都要获得该锁,确保在同一时刻只有一个线程可以访问到这个变量。若类的不变约束涉及多个状态变量,那么还需要一个附加需求:每个参与到不变约束的变量由同一个锁来守护。

 

0 0
原创粉丝点击