关于“竞态条件”的理解详见http://en.wikipedia.org/wiki/Race_condition
转载文章:
[本文是我对Java Concurrency In Practice第二章的归纳和总结, 转载请注明作者和出处, 如有谬误, 欢迎在评论中指正. ]
多线程环境下,无需调用方进行任何同步处理也能保证正确性的类是线程安全的类
无状态的对象是线程安全的。无状态是指没有成员变量。由于方法的局部变量都是在线程私有的栈中分配的,因此在一个线程中调用无状态对象的方法,不会影响到其他线程。
race condition: 正确性依赖于事件发生的相对时间。
check-and-act是race condition中的一种,指的是基于check的结果进行操作。由于check和act并非是原子的,进行act时check的结果可能已经无效,那么基于check所进行的act就可能带来问题。
见如下的lazy单例类:
- public class LazyInitRace {
- private static ExpensiveObject instance = null;
- public static ExpensiveObject getInstance() {
- // if语句是一个check-and-act操作
- if (instance == null) {
- instance = new ExpensiveObject();
- }
- return instance;
- }
- }
这是一个check-and-act的典型例子:首先判断instance是否为null,如果是就创建ExpensiveObject对象,否则直接 返回instance。但是判断和创建并非原子操作,假设线程1判断出instance为null,要是另一个线程紧接着创建了 ExpensiveObject对象,那么线程1的判断就失效了,基于判断结果所进行的创建操作就会导致程序中存在多个ExpensiveObject对 象--这违背了单例模式的初衷。
Read‐modify‐write也是race condition中的一种,指的是读取某个变量的值,修改后写回。这显然不是一个原子操作,如果B线程在A线程read之后write之前修改了变量的 值,那么A线程read的结果就失效了,基于read所做的modify就可能带来问题。
见如下的servlet:
- public class CountingFactorizer implements Servlet {
- private final long count = 0;
- public void service(ServletRequest req, ServletResponse resp) {
- BigInteger i = extractFromRequest(req);
- BigInteger[] factors = factor(i);
- // // 自增语句是一个Read‐modify‐write操作
- count++;
- encodeIntoResponse(resp, factors);
- }
- }
使用java提供的同步机制,将check-and-act或者Read‐modify‐write转换为原子操作,则可以避免race condition造成的错误。
使用同步机制改进LazyInitRace和CountingFactorizer类,使之成为线程安全的类:
- public class LazyInitRace {
- private static ExpensiveObject instance = null;
- public static ExpensiveObject getInstance() {
- // 使用synchronized将check-and-act操作转换为原子操作
- if (instance == null) {
- synchronized (LazyInitRace.class) {
- if (instance == null) {
- instance = new ExpensiveObject();
- }
- }
- }
- return instance;
- }
- }
- public class CountingFactorizer implements Servlet {
- private final AtomicLong count = new AtomicLong(0);
- public void service(ServletRequest req, ServletResponse resp) {
- BigInteger i = extractFromRequest(req);
- BigInteger[] factors = factor(i);
- // 使用long对应的原子类AtomicLong,将Read‐modify‐write操作转换为原子操作
- count.incrementAndGet();
- encodeIntoResponse(resp, factors);
- }
- }
对于只有一个成员变量的对象,成员的状态就是对象的状态。如果涉及到该成员的操作都是原子的,那么该类就是一个线程安全的类。比如以上的 LazyInitRace类,其只有一个成员变量instance,且涉及instance的操作是原子的,因此LazyInitRace类是一个线程安 全的类。
是否可以类推出,只要类的每一个成员变量的操作都是原子的,类就是线程安全的?不行。
因为每一个成员的操作都是原子的,不能保证所有涉及到成员的操作整体上是原子的。例如:
- public class UnsafeCachingFactorizer implements Servlet {
- private final AtomicReference<BigInteger> lastNumber = new AtomicReference<BigInteger>();
- private final AtomicReference<BigInteger[]> lastFactors = new AtomicReference<BigInteger[]>();
- public void service(ServletRequest req, ServletResponse resp) {
- BigInteger i = extractFromRequest(req);
- // 优先使用缓存
- if (i.equals(lastNumber.get()))
- encodeIntoResponse(resp, lastFactors.get() );
- else {
- BigInteger[] factors = factor(i);
- // 将最后一次的计算结果缓存起来
- lastNumber.set(i);
- lastFactors.set(factors);
- encodeIntoResponse(resp, factors);
- }
- }
- }
UnsafeCachingFactorizer类的2个成员lastNumber和lastFactors的set()和get()方法是原子的,但 是该类不是线程安全的。因为从整体上看,2次写入和2次读取都不是同时进行的,UnsafeCachingFactorizer类仍然存在race condition.
java的synchronized机制使用的锁是可重入锁,即同一个线程可以多次申请持有同一把锁而不会引起死锁。假设A线程持有lock,那么 如果B线程申请lock锁,B线程就会被阻塞。但是如果A线程再次申请已经持有的lock锁,该申请将获得通过,这就是所谓的同一线程可多次获取同一把 锁。作为锁的对象,不仅需要标识锁的所有者,也需要标识所有者持有的计数。如果所有者线程刚刚申请到锁,则计数器的值为1,每重新获取一次,计数器的值加 1,每退出一个同步代码块,计数器的值减1. 当计数器的值减为0时,所有者线程才释放锁。可重入锁的设计是为了防止因申请已持有的锁而造成死锁,例如:
- public class Widget {
- public synchronized void doSomething() {
- ...
- }
- }
- public class LoggingWidget extends Widget {
- public synchronized void doSomething() {
- System.out.println(toString() + ": calling doSomething");
- super.doSomething();
- }
- }
如果java中的锁不是可重入的,那么调用LoggingWidget对象的doSomething方法就会导致死锁。
相关推荐
Java Concurrency in Practice 英文无水印pdf pdf所有页面使用FoxitReader和PDF-XChangeViewer测试都可以打开 本资源转载自网络,如有侵权,请联系上传者或csdn删除 本资源转载自网络,如有侵权,请联系上传者...
<<java并行编程>>英文版chm格式,英文名称<Java Concurrency in Practice>,一直想买这本书,但总是缺货,找到了电子版,分享给大家。 Java Concurrency in Practice By Brian Goetz, Tim Peierls, Joshua Bloch,...
Using the concurrency building blocks in java.util.concurrent Performance optimization dos and don'ts Testing concurrent programs Advanced topics such as atomic variables, nonblocking algorithms, ...
《Java并发编程实践》是Java开发者必读的经典之作,由Brian Goetz等多位专家共同撰写。这本书深入浅出地探讨了Java平台上的并发问题,帮助读者理解和掌握如何编写高效、可靠且可维护的多线程应用程序。以下是该书...
Java Concurrency in practice
Java Concurrency in Practice JAVA并发编程实践中文版(全)第二部分
《Java Concurrency in Practice》是Java并发编程领域的一本经典著作,由Brian Goetz、Tim Peierls、Joshua Bloch、Joseph Bowles和Doug Lea等专家共同编写。这本书深入探讨了Java平台上的多线程和并发编程,旨在...
java concurrency in practice 经典的多线程编程书籍,英文版
本笔记将深入探讨《Java Concurrency In Practice》这本书中的核心概念,结合Guava库的实际使用案例,帮助读者理解并掌握Java并发编程的精髓。 首先,我们来了解Java并发的基础知识。Java提供了丰富的并发工具类,...
首先,"Java Concurrency in Practice"是Java并发编程的经典之作,由Brian Goetz、Tim Peierls、Joshua Bloch、David Holmes和Doug Lea合著。这本书提供了一套实用的指导原则、设计模式和最佳实践,帮助Java开发者...
《Java Concurrency In Practice》是一本关于Java并发编程的经典著作,由Brian Göetz、Tim Peierls、Joshua Bloch、Joseph Bowbeer、David Holmes和Doug Lea共同编写。本书深入探讨了Java平台上的多线程编程技巧,...
java_concurrency_in_practice.pdf jcip-examples-src.jar jcip-annotations-src.jar 英文版是高清晰的,实战和实践都是同一帮人对英文版书的翻译,网传实战的翻译质量更好,实战是2012年出版的,应该是对前一版实践...
- **书名**:《Java并发实践》(Java Concurrency in Practice) - **作者**:Brian Goetz, Tim Peierls, Joshua Bloch, Joseph Bowbeer, David Holmes, Doug Lea - **出版社**:Addison Wesley Professional - **...
这本书很出名吧,大家都知道吧,我靠,20个字的描述咋这么累啊。
If you are a java developer, you should read this book. It will bring a lot benifit to you.
《JAVA并发编程实践》随着多核处理器的普及,使用并发成为构建高性能应用程序的关键。Java 5以及6在开发并发程序中取得了显著的进步,提高了Java虚拟机的性能以及并发类的可伸缩性,并加入了丰富的新并发构建块。在...