《Java并发编程的艺术》勘误和支持

本书源码下载地址

第一版第一次印刷勘误

第2页,thread.join();这句代码和上面一行代码交换位置。
第3页,第一个表格“并发比串行快多少”的第五行,慢=>差不多。
第9页,表2-1第三行缓存行。第一个缓存=》CPU高速缓存,缓存线=》缓存行,
第9页,表2-1第三行缓存行。需要使用多个主内存读周期=》现代CPU需要执行几百次CPU指令
第18页,代码少了第一行 public class Counter {

第一版第二次印刷勘误

第一版第三次印刷勘误

第一版第四次印刷勘误

  • 第168页LinkedBlockingQueue 有界阻塞队列 修改成 无界阻塞队列。
  • 第5页的源码,DeadLockDemo里面 private static String A = “A”; 这一行的private拼写少了个e。
  • 第112页的源码,work.get(count)后面少了个分号;。
  • 第186页的源码,代码第2、3行,user应改为User。
  • 第210页的图,图10-2左上角的类应该是Runnable;图10-3中有2个create漏了字母e,1个execute漏了字母2(找晓明修改下)
  • Trackback 关闭
  • 评论 (73)
    • 夜阳
    • 2015/08/11 12:11上午

    18页代码少了第一行public class Counter {

    76页表3-9把表3-8复制了一遍,完全错误

    • 多谢勘误

        • 夜阳
        • 2015/08/11 10:17上午

        另外问下哪可以下载程序源码呢
        有几个地方代码都不全…

      • 并发网可以下载

        • xlyslr
        • 2016/02/24 12:08上午

        76页表3-9修正的结果希望贴出来

    • 下雨不打伞
    • 2015/09/06 9:17上午

    第5页的源码,DeadLockDemo里面 private static String A = “A”; 这一行的private拼写少了个e

    • abyjun
    • 2015/09/13 9:01下午

    请问,37页的图中的读事物写的是读高32位,是不是应该改为读整个long型变量

    • yangcheng33
    • 2015/10/07 5:56下午

    210页,图10-2左上角的类应该是Runnable;图10-3中有2个create漏了字母e,1个execute漏了字母2
    186页,代码第2、3行,user应改为User

    • yangcheng33
    • 2015/10/08 1:40上午

    112页,代码倒数第12行,漏掉了分号

    • 爪云
    • 2015/12/28 9:55上午

    31页, 第五行, volaLile 拼写错误, 应该是volatile

    • wuxiaolinchn
    • 2016/01/17 10:44上午

    5页,1.2死锁代码,2个重写Runnable接口的run方法都写成了publicvoid,两个关键字之间缺少空格。

    • qiguan123
    • 2016/01/27 7:24下午

    164页最后一行,应该是“如果p的next节点是null”吧。

    • superheizai
    • 2016/02/01 12:06下午

    59页,第三章,3.6.5标题,“溢出”==》“逸出”
    69,第三种,3.8.2第一行,(instance=new Singleton();),实际上例子给的是(instance= new Instance():)

    • superheizai
    • 2016/02/02 9:38上午

    虽然么有回复,但是我要继续报错:)
    第五章,5.2.2中5.自定义同步组件-TwinsLock部分,关于资源数的描述部分,“status为2”应该是“state为2”。
    第六章, 6.1.3的2.初始化segmentShift和segmentMask,其中”segmentSHift最大值是16″,不是最大值,是最小值。

    • 魏 鹏
    • 2016/04/06 11:28上午

    第三页

    Content Switch –> Context Switch

      • yoxibaga
      • 2016/10/06 4:58下午

      魏老师,请教一个问题,第四章最后【基于线程池的简单Web服务器】的demo,server启动之后,URL如何写呢?basepath = ‘F:\\workspace’

    • xiaohaizi
    • 2016/04/27 8:51下午

    你好 ,第4.4.4小节,115页 代码中 out.flush() 应该在socket.getOutputStream().write(array,0,array.length);之前调用,
    否则,内容会先于报文头发送。

    • sirding
    • 2016/09/08 5:20下午

    338页
    “因为CyclicBarrier设置了拦截线程的数量是2,所以必须等代码中的第一个线程和线程A 都执行完之后,才会继续执行主线程,然后输出2,所以代码执行后的输出如下。”

    逻辑应该是A线程一定是先执行的,但第一个线程和主线程的执行顺序是不一定的。所以执行的结果应该3、2、1或是3、1、2

    • MindHacks
    • 2016/12/21 9:40下午

    您好,第12页,第5行,第8行的Synchronized拼写错误,被拼成了Synchonized

    • 16789Mauersu
    • 2017/01/12 5:09下午

    对P134页的TwinsLock的unlock方法存疑

    public boolean tryReleaseShared(int returnCount) {
    for(;;) {
    int current = getState();
    int newCount = current + returnCount;
    if(compareAndSetState(current, newCount)) {
    return ture;
    }
    }
    }
    unlock方法及调用方法,都没有对当前线程进行判断,判断其是否持有锁,我觉得应该加上这个判断,因为只有持有所得线程才能释放锁

      • 16789Mauersu
      • 2017/01/12 5:14下午

      而且误操作,会导致整个锁状态处于异常状态

        • 16789Mauersu
        • 2017/01/12 5:17下午

        同理P123的Mutex类

        • helloworld
        • 2017/02/13 9:45下午

        不好意思哇,以上言论全部有误,个人没法删除,麻烦管理员帮忙删除下,以防误导其他人,谢啦

    • yanhui
    • 2017/02/11 6:10下午

    刚看书到第3页,看过表1-1的表格后,有点不太懂。

    书上说当执行并发累加不超过百万时,速度要比串行要慢。

    为什么超过百万时,并行的速度要快近一倍呢?是不是CPU的双核同时运行的结果。如果在单核CPU上,分时调度的结果一定是慢的啊。

    请各位大神给予解答,谢谢。

    • helloworld
    • 2017/02/15 4:42下午

    文字错误
    P147
    表5-12
    对比项列“当前线程释放锁并进入等待状态,在等待状态中不响应中断”疑似错误
    应该为“当前线程释放锁并进入等待状态,在等待状态中响应中断”

      • helloworld
      • 2017/02/15 4:43下午

      多了个“不”字

        • helloworld
        • 2017/02/15 4:51下午

        不好意思哇,以上言论再次全部有误,个人没法删除,麻烦管理员帮忙删除下,以防误导其他人,谢啦 🙁

    • style920
    • 2017/03/03 3:51下午

    版次: 2016年10月份第一版第六次印刷
    第七章 java中的13个原子操作类
    只有12个,在7.2原子更新数据AtomicIntegerArray重复

    • 猪会飞
    • 2017/03/05 7:53下午

    表1-1测试结果中:循环次数为10万次的这行,并发比串行快多少的结果应该是“快”吧???

    • zlsf
    • 2017/03/23 10:38下午

    您好,正在拜读该书。43页给予保守策略的…..屏障插入策略。下面4点,最后两条有2个volatile读操作后,的规则。是不是应该有一个是读前,一个是读后

      • zlsf
      • 2017/03/23 10:44下午

      是我理解错误,没看到下面的内容,请忽略这个反馈。抱歉

        • thomas-tang
        • 2020/05/01 4:22下午

        倒数第二个,应该是:在每个volatile读操作的前面插入一个LoadLoad屏障吧。没看明白

        • thomas-tang
        • 2020/05/01 4:51下午

        看到P44图,明白了。ps:网上一些博文都是写的前面,跟本书不一致。

    • lt199025
    • 2017/05/28 11:41上午

    你好,目前正在拜读该书,发现一个问题
    版次:2016年4月第1版第5次印刷
    描述:第2章,第10页的第7行,提到8.1.4节,但我没在该书中找到相应的的章节,不知是不是印刷的问题,如果是,那么应该是哪个章节

    • wkzq
    • 2017/09/24 10:05上午

    第一版第一次印刷
    146页第四行“RentrantReadWriteLock”少了个e

    • sanshiz
    • 2017/11/08 11:09下午

    01,案例代码太简单,排版不好
    02,注释有很多欠缺,并且注释有的地方很晦涩

    • sanshiz
    • 2017/11/09 1:14上午

    ScheduledThreadPoolExecutor, ThreadPoolExecutor, 作者你確定都有submit()方法,10章裏面很多地方這樣寫,比如p212

    • bingqiang.lin
    • 2018/02/05 5:50下午

    您好,版次:2017年7月第1版第9次印刷,P134,TwinsLock中的tryAcquireShared(int reduceCount)方法,缺少返回语句。

      • bingqiang.lin
      • 2018/02/05 5:53下午

      Sorry, 是我大意了。

    • like
    • 2018/05/03 6:44下午

    版次:2017年7月第1版第9次印刷,P158,“segmentShift等于32减sshift,所以等于28。”和后边的“所以segmentShift最大值是16”前后矛盾

    • like
    • 2018/05/06 11:17上午

    P193,代码清单8-4的执行结果还有一种可能是3 2 1,只等线程A,并不等待第一个线程

    • yuanye3486
    • 2018/09/01 4:10下午

    P39,有部分内容让我感到疑惑,原书内容如下:

    简而言之,volatile变量自身具有下列特性。

    原子性:对任意单个volatile变量的读/写具有原子性,但类似于volatile++这种复合操作不
    具有原子性。

    在我的理解中 volatile 不是只能保证变量的可见性,不能保证其原子性吗?
    希望作者能帮助解答,非常谢谢!

    • stonej
    • 2019/04/25 2:54下午

    您好:正在拜读 并发编程的艺术

    关于第3章JMM中 3.1.4小节中的 表3.3 内存屏障类型表 有些不明白的地方

    1.关于4中类型的屏障 涉及到的 Load 指令是否是指从主存中读取更新线程自己的工作内存中

    2.Load1 LoadLoad Load2 屏障中的Load1 指的是一条load指令 还是屏障上面的涉及到的所有Load 指令

    3.Store1 StoreLoad Load2 屏障 书中描述会把屏障之前的store操作刷新到主存 那么是否也会把storeLoad屏障之前的Load 从主存中加载到工作线程中 。

    • eggo
    • 2019/05/16 10:02上午

    尊敬的作者:
    您好!我正在拜读您的《Java并发编程的艺术》,第四章的4.2.3 理解中断一节,Interrupted程序的执行结果与书中的并不一致,SleepThread程序的打印结果为:SleepThread interrupted is true。不知是否是在java12.0.1版本对InterrupedException抛出之前清除中断标志位的机制修改了,还请在您有时间的时候解答以下。

    • eggo
    • 2019/05/18 5:07下午

    尊敬的作者:
    您好!我正在拜读您的《Java并发编程的艺术》,正如您在4.1.5 Daemon线程一节所说的,不能把执行关闭或者清理资源的逻辑放到Daemon线程的finally块中。
    然而,在第5章第5节——自定义同步组件,在程序TwinsLockTest.java程序中,在Worker的finally块中使用了lock.unlock();语句,这样做的理由是什么呢?

    • Jasonrale
    • 2019/07/21 5:59下午

    作者您好,我正在读这本书,我发现第52页,也就是3.5.3节,其中ReentrantLock的unlock()方法的调用轨迹顺序写错了,应该先Sync:release(int arg),然后AQS:tryRelease(int releases)。

      • 842036133@qq.com
      • 2019/07/31 3:54下午

      你好,我刚也在看这节,我看的JDK8的源码,作者写的我觉得没有错,因为Sync抽象类是继承AQS的,所以作者说先调用AQS的方法应该也可,在调用链的最后,调用了ReentrantLock的静态内部类sync的方法tryRelease,因为静态内部类Sync继承AQS,重写了tryRelease方法,所以最后调用的是Sync的tryRelease方法

        • 842036133@qq.com
        • 2019/08/19 1:33下午

        题主我重新整理下ReentrantLock的unlock()方法的调用链,觉得和书上写的吻合,unlock方法调用其静态内部类Sync的release()方法,此方法没有重写,实际是调用AQS的release方法,之后在AQS的release方法内调用了ReentrantLock的静态内部类Sync重写的tryRelease方法,所以顺序是1.ReentrantLock.lock() 2.AQS.release() 3.Sync.tryRelease(),公平锁和非公平锁的调用链相同

    • webber
    • 2019/07/28 6:28下午

    volatile写前面加StoreStore内存屏障,可以禁止volatile前面的普通写和volatile变量重排序,但是普通读变量感觉是没有办法禁止重排序的?

      • 842036133@qq.com
      • 2019/07/31 3:57下午

      个人认为是volatile写后面加的storeload屏障保证了写之前的所有操作对之后的读操作可见(实现是禁止了写之前的所有操作重排序到写之后,包括普通读变量的操作)

    • cy503328434
    • 2019/08/06 1:56下午

    你好。2018年10月第一版第13次印刷,158页 2.初始化segmentShift和segmentMask 本段的倒数第二行。“所以segmentShift”最大值16 应该是最小值是16吧。

    • twx626
    • 2019/11/04 10:24上午

    作者,您好,请教个问题,书中85页到86页里的代码清单4-2 Priority.java中,Job.jobCount这个字段如何保证可见性的?

    • Alice
    • 2020/04/21 3:00下午

    第八章Java中的并发工具类-8.4线程交换数据的Exchanger。 代码8-8 ExchangerTest的代码。图中标注的位置B 不能加引号,否则交换的内容就错了

    • Alice
    • 2020/04/21 3:01下午

    第三章中3.8.4基于类初始化的解决方案的小节中发现:在类初始化—第3阶段的执行时序表(表3.-9)与图3-43 类初始化-第3阶段的图不符合。

    • Alice
    • 2020/04/21 3:02下午

    在第三章中3.8.4基于类初始化的解决方案的小节中发现:在类初始化—第3阶段的执行时序表 。表3-9 与表3-8(类初始化-第2阶段的执行时序图)是一模一样的。应该是 有区别的,图错了

      • yjh
      • 2021/07/21 8:40上午

      Alice :
      在第三章中3.8.4基于类初始化的解决方案的小节中发现:在类初始化—第3阶段的执行时序表 。表3-9 与表3-8(类初始化-第2阶段的执行时序图)是一模一样的。应该是 有区别的,图错了

      刚读到这部分类容,发现表格一致,虽然没有官方解释,但从图能猜个大概。

    • Logon
    • 2020/05/11 11:25上午

    2019年11月第1版第16次印刷,第43页,中间偏上位置。
    在每个volatile读操作的后面插入一个LoadLoad屏障。
    应该是在前面吧,后两条都是在后面。

      • Logon
      • 2020/05/11 11:44上午

      在每个volatile读操作的后面插入一个LoadLoad屏障。
      在每个volatile读操作的后面插入一个LoadStore屏障。
      没错的,是我没理解,抱歉,打扰了。

    • shuishui
    • 2020/05/27 4:11下午

    有volatile变量修饰的共享变量进行写操作的时候会多出第二行汇编代码,通过查IA-32架
    构软件开发者手册可知,Lock前缀的指令在多核处理器下会引发了两件事情[1]。
    1)将当前处理器缓存行的数据写回到系统内存。
    2)这个写回内存的操作会使在其他CPU里缓存了该内存地址的数据无效。

    这个2是如何使其他cpu里的缓存无效的呢

      • shuishui
      • 2020/05/27 4:14下午

      看到只有这个解释

      为了提高处理速度,处理器不直接和内存进行通信,而是先将系统内存的数据读到内部
      缓存(L1,L2或其他)后再进行操作,但操作完不知道何时会写到内存。如果对声明了volatile的
      变量进行写操作,JVM就会向处理器发送一条Lock前缀的指令,将这个变量所在缓存行的数据
      写回到系统内存。但是,就算写回到内存,如果其他处理器缓存的值还是旧的,再执行计算操
      作就会有问题。所以,在多处理器下,为了保证各个处理器的缓存是一致的,就会实现缓存一
      致性协议,每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当
      处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状
      态,当处理器对这个数据进行修改操作的时候,会重新从系统内存中把数据读到处理器缓存
      里。

    • zhangxd
    • 2020/06/01 12:36下午

    第112页,不只是少了一个分号,workers.remove(worker) 之后 workers 数组长度会变小,使用workers.get(count) 明显会下标越界。

    • 01
    • 2020/06/01 5:00下午

    飞哥你好: 最近在看您的《java并发编程艺术》一书;有一些个人感觉可能存在问题的地方。
    一:在读写锁实现章节中代码清单5-19 processData方法代码上感觉有点漏洞,如果在第二个获取读锁前抛出异常;这样第二个读锁释放锁就会出问题;个人感觉可以修改如下:
    public void lockDowngrade() {
    // 注意这里的
    readLock.lock();
    try {

    System.out.println(“当前线程获取写锁”);
    writeLock.lock();
    try {
    System.out.println(“准备获取读锁,进行数据操作”);
    } finally {
    System.out.println(“释放写锁,如果当前线程获取有读锁,锁降级”);
    readLock.lock();
    readLock.unlock();
    writeLock.unlock();
    }
    System.out.println(“降级读锁操作”);
    } finally {
    System.out.println(“释放读锁”);
    readLock.unlock();
    }

    }

    二: FutureTask 现在的源码中对锁的实现不是利用的AQS了;可以将代码更新下;而是利用的AQS 原理自己维护了一个阻塞队里;在利用共享锁和条件锁实现的强占和唤醒。

    • MonkeyBrother
    • 2020/07/22 10:11下午

    您好!在本书的109页中的 ConnetionRunner 是不是应该是 ConnectionRunner.虽然不影响程序的正常运行,但是作为一本颇有影响力的书籍应该单词拼写正确的。
    还有个建议就是 希望能够在实现接口或者继承的时候,是的方法也好,还是重写的也好,能够按照规范加上 @Override 这样更加能清楚的区分。

    • luser
    • 2020/08/18 11:05上午

    作者您好!
    书中213页,DefaultThreadPool中的p
    ublic void removeWorker(int num) {
    synchronized (jobs) {
    if (num >= this.workerNum) {
    throw new IllegalArgumentException(“beyond workNum”);
    }/
    / 按照给定的数量停止Worker
    int count = 0;
    while (count < num) {
    Worker worker = workers.get(count)
    if (workers.remove(worker)) {
    worker.shutdown();
    count++;
    }
    }t
    his.workerNum -= count;
    }
    }方法是否存在workers.get(count)数组下标越界呢

    • tlcxhw
    • 2020/10/05 3:32下午

    作者你好!
    请教一个问题,在代码清单4-16 ConnectionPool.java中,fetchConnection函数的超时等待模式实现,使用while循环的意义在哪里?
    在知道超时等待的时间之后,调用pool.wait(remaining)本身不就具备了超时等待的功能吗?此函数调用返回无非2种情况:1)等待时间超时;2)持有pool监视器的其他线程调用notify/notifyAll函数,并释放了监视器。第1种情况下,remaining会被更新为0,直接就跳出循环。第2种情况下,pool中被放回了一个元素,肯定也不为空,同样会跳出循环。这样的话,while循环其实都会仅被执行1次。为什么不能直接通过调用pool.wait(remaining)函数来实现呢?
    不知道我的理解对不对,望不吝答疑,感谢!

    • lili
    • 2020/10/12 2:40下午

    作者,您好
    在阅读到第4章,4.1.4线程的状态,图4-1 java线程状态变迁。waiting线程经过Object.notify()后状态变为runnable。
    我在网上找到一些资料:
    资料1:https://www.uml-diagrams.org/java-thread-uml-state-machine-diagram-example.html
    资料2:https://stackoverflow.com/questions/28378592/java-thread-state-transition-waiting-to-blocked-or-runnable
    在资料1中的图片,waitting线程调用被notify后的状态是blocked。
    资料2中相应的回答,也表明了waitting线程调用被notify后的状态是blocked
    所以,跟《java并发编程的艺术》的内容有矛盾的地方。
    望作者答疑,谢谢。

    • luoshu
    • 2020/11/08 11:26上午

    20页
    3.1.1 最后两段
    同步是指程序中用于控制不同线程间操作发生相对顺序的机制。在【共享内存并发模型里,同步是显式进行的】。程序员必须显式指定某个方法或某段代码需要在线程之间互斥执行。在消息传递模型的并发模型里,由于消息的发送必须在消息的接收之前,因此同步是隐式进行的。
    【Java的并发采用的是共享内存模型,Java线程之间的通信总是隐式进行】,整个通信过程对程序员完全透明。如果编写多线程程序的Java程序员不理解隐式进行的线程之间通信的工作机制,很可能会遇到各种奇怪的内存可见性问题。

    ——————————–
    如上两端文字,两个【】包裹之间的内容自相矛盾。显然大家可能也知道我大lowb java是基于共享内存模型显式通信的。但自相矛盾的文字容易对新手造成一定影响。

    • qazxsw
    • 2021/01/26 7:49下午

    2.1 volatile的应用
    1.volatile的定义与实现原理
    1)Lock前缀指令会引起处理器缓存回写到内存
    “如果访问的内存区域已经缓存在处理器内部,则不会声言LOCK#信号。”——我不确定“声言”对不对,感觉应该是“声明”?

      • sysuwsh
      • 2021/02/28 3:27下午

      我也觉得这里有问题…也没查到声言这个词

    • present
    • 2021/07/29 9:23上午

    多字或者缺字
    p136 5.3重入锁
    第一自然段第二行
    “除此之外,该锁【的】还支持获取锁时的公平和非公平性选择”。

    • Sheen
    • 2021/09/20 3:52下午

    作者您好,请问43页提到的JMM内存屏障插入策略中:
    在每个volatile读操作的后面插入一个LoadLoad屏障。
    在每个volatile读操作的后面插入一个LoadStore屏障。
    这两句话中是否有一句‘后面’应改为‘前面’呢?

return top