1 / 32

Java并发编程的常见陷阱

Java并发编程的常见陷阱. boyan@taobao.com(伯岩). 不要把并发当成万能锤子. 多线程 != 性能提升. Amdahl定律. 。 如果F是必须 串行化执行的比重 ,那么Amdahl定律告诉我们,在一个N 处理器的机器中 , 我们最多可以加速 :. 正确使用读写锁. 读时不能写 写时不能读 可以并发读 不能并发写 读写比例高. 典型错误——LRUMap简单实现. 继承LinkedHashMap,enableLRU设置为true,覆写removeEldestEntry方法 , 使用读写锁同步 读时不能写 √ 写时不能读 √

wenda
Download Presentation

Java并发编程的常见陷阱

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Java并发编程的常见陷阱 boyan@taobao.com(伯岩)

  2. 不要把并发当成万能锤子 • 多线程 != 性能提升

  3. Amdahl定律 • 。如果F是必须串行化执行的比重,那么Amdahl定律告诉我们,在一个N处理器的机器中 • ,我们最多可以加速:

  4. 正确使用读写锁 • 读时不能写 • 写时不能读 • 可以并发读 • 不能并发写 • 读写比例高

  5. 典型错误——LRUMap简单实现 • 继承LinkedHashMap,enableLRU设置为true,覆写removeEldestEntry方法, • 使用读写锁同步 • 读时不能写√ • 写时不能读√ • 可以并发读Ⅹ • 不能并发写 √ • 读写比例高 √

  6. 在构造函数中启动线程 • 继承带来的隐患

  7. 问题 • 假设A的构造函数中启动某个线程,该线程读取A中的实例变量i • B继承A,并在构造函数中重新给i赋值 • 问题: B的实例初始化,首先初始化父类A,启动线程,线程此时读取的i非B所期望。 • 解决: • 1、不允许继承——final • 2、单独的start方法(推荐)

  8. 正确使用wait/notify • 一个世界在等待

  9. 常见问题 • 代码: • 问题: • 未同步 • If替代while——虚假唤醒 • notify替代notifyAll——被遗忘的线程

  10. Atomic+Atomic!=Atomic • MethodA is thread-safe • MethodBis thread-safe • 组合起来还是线程安全的吗? • public void methodC(){ • MethodA(); • MethodB(); • }

  11. 我要同步 • 容器是同步的,就没有问题了吗? • 同步的不是容器,而是寂寞

  12. 正确处理中断 • 将中断进行到底

  13. Thread.interrupt()干什么了? • 设置中断状态 • 中断阻塞操作

  14. How • wait,sleep,join都是nativemthod,直接抛出InterruptedException • InterruptibleChannel,关闭连接, • 抛出ClosedByInterruptException(AbstractInterruptibleChannel.java):

  15. 错误案例1 • 取消任务,取消不了?

  16. 错误案例2 • 吞掉中断,上层代码怎么办?

  17. 错误案例3 • 包装成Runtime异常?

  18. 取消任务的正确做法 • 没有阻塞操作, volatile状态变量 • 不响应中断的阻塞操作,如socket.read()之类,关闭socket。 • 响应中断的阻塞操作(如sleep,wait,join等),推荐状态变量+捕捉中断异常

  19. 处理InterruptedException • 除非你明确知道你在干什么,否则不要简单地catch并忽略 • 声明Check异常,继续抛出,交给他人处理 • 重设中断状态,让上层代码发现中断状态。

  20. 嫁错郎,加错锁 • 女怕嫁错郎

  21. Don't • #1: Don’t synchronize on an object you’re changing • #2: Don’t synchronize on a String literal • #3: Don’t synchronize on auto-boxed values • #4: Don’t synchronize on null • #5: Don’t synchronize on a Lock object • #6: Don’t synchronize on getClass() • #7: Be careful locking on a thread-safe object with • encapsulated locking

  22. 错误案例1 • 只把杭州当汴州 • 问题: • foo是null? • 改变了foo指向的对象,加锁形同虚设

  23. 错误案例2 • 躲猫猫 • 问题: • Bar跟Foo的test方法中的锁不一样。

  24. 错误案例3 • 对同步的容器加锁,使用的锁与容器内部使用的锁不一定是同一个。 • HashTable、Vector√ • ConcurrentHashMap X

  25. 不一致的同步 • int的读写,问题不是太大

  26. 邂逅Map • 忘记我的另一半,不可原谅

  27. 阿甘,先别run • 美国犀利哥

  28. Start Vs. Run • 哥只是爱跑步 • 问题: • http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4533087 • 在jdk1.4.2以前,每个thread初始化都会加入ThreadGroup,如果你只是调用run而不是 • start,那么此thread将无法被正常回收造成内存泄漏。在JDK5之后,将加入ThreadGroup • 这一步从构造函数转移到start方法,因此不会有问题。

  29. 正确使用Volatile • Volatile能做什么? • 状态标识,如取消任务线程 • 安全发布,如修复DLC问题 • 开销较低的读写锁

  30. 正确使用Volatile • Volatile不能做什么? • 不能用于做计数器 • 与其他变量构成不变式

  31. 规避j.u.c的bug • LinkedBlockingQueue.poll(time,timeUnit) 内存泄漏,其他queue也有类似问题 • http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6460501 • Semaphore.tryAcquire内存泄漏甚至hang住 • http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6460501 • http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6241823 • ReentrantReadWriteLock可能在没有任何线程持有锁的情况下被hang住,这是 • 一系列的BUG: • http://bugs.sun.com/view_bug.do?bug_id=6822370 • http://bugs.sun.com/view_bug.do?bug_id=6903249 • 可以说j.u.c在1.5这个版本上有非常多的bug,具体可以查看下sun的bug database • ,推荐升级jdk到最新稳定版。

  32. End,Thanks

More Related