Successfully reported this slideshow.
Java并发编程的常见陷阱<br />boyan@taobao.com(伯岩)<br />

不要把并发当成万能锤子<br /><ul><li>多线程 != 性能提升</li></li></ul><li>Amdahl定律<br /><ul><li>。如果F是必须串行化执行的比重,那么Amdahl定律告诉我们,在一个N处理器的机器中
,我们最多可以加速:</li></li></ul><li>正确使用读写锁<br /><ul><li>读时不能写
写时不能读
可以并发读
不能并发写
读写比例高</li></li></ul><li>典型错误——LRUMap简单实现<br /><ul><li>继承LinkedHashMap,enableLRU设置为true,覆写removeEldestEntry方法,
使用读写锁同步
读时不能写√
写时不能读√
可以并发读Ⅹ
不能并发写   √
读写比例高   √</li></li></ul><li>在构造函数中启动线程<br /><ul><li>继承带来的隐患</li></li></ul><li>问题<br /><ul><li>假设A的构造函数中启动某个线程,该线程读取A中的实例变量i
B继承A,并在构造函数中重新给i赋值
问题: B的实例初始化,首先初始化父类A,启动线程,线程此时读取的i非B所期望。
解决:
      1、不允许继承——final
      2、单独的start方法(推荐)</li></li></ul><li>正确使用wait/notify<br /><ul><li>一个世界在等待</li></li></ul><li>常见问题<br /><ul><li>代码:
问题:
未同步
If替代while——虚假唤醒
notify替代notifyAll——被遗忘的线程</li></li></ul><li>Atomic+Atomic!=Atomic<br /><ul><li>MethodA is thread-safe
MethodBis thread-safe
组合起来还是线程安全的吗?
public void methodC(){
MethodA();
MethodB();
}</li></li></ul><li>我要同步<br /><ul><li>容器是同步的,就没有问题了吗?
同步的不是容器,而是寂寞</li></li></ul><li>正确处理中断<br /><ul><li> 将中断进行到底</li></li></ul><li>Thread.interrupt()干什么了?<br /><ul><li>设置中断状态
中断阻塞操作</li></li></ul><li>How<br /><ul><li>wait,sleep,join都是nativemthod,直接抛出InterruptedException
InterruptibleChannel,关闭连接,
抛出ClosedByInterruptException(AbstractInterruptibleChannel.java):</li></li></ul><li>错误案例1<br /><ul><li>取消任务,取消不了?</li></li>...
Upcoming SlideShare
Loading in …5
×

Java多线程常见陷阱

6,878 views

Published on

杭州交流的PPT

Published in: Technology
  • 谢谢分享啊!我发现淘宝的人很喜欢这个网站啊,呵呵
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

Java多线程常见陷阱

  1. 1. Java并发编程的常见陷阱<br />boyan@taobao.com(伯岩)<br />
  2. 2. 不要把并发当成万能锤子<br /><ul><li>多线程 != 性能提升</li></li></ul><li>Amdahl定律<br /><ul><li>。如果F是必须串行化执行的比重,那么Amdahl定律告诉我们,在一个N处理器的机器中
  3. 3. ,我们最多可以加速:</li></li></ul><li>正确使用读写锁<br /><ul><li>读时不能写
  4. 4. 写时不能读
  5. 5. 可以并发读
  6. 6. 不能并发写
  7. 7. 读写比例高</li></li></ul><li>典型错误——LRUMap简单实现<br /><ul><li>继承LinkedHashMap,enableLRU设置为true,覆写removeEldestEntry方法,
  8. 8. 使用读写锁同步
  9. 9. 读时不能写√
  10. 10. 写时不能读√
  11. 11. 可以并发读Ⅹ
  12. 12. 不能并发写 √
  13. 13. 读写比例高 √</li></li></ul><li>在构造函数中启动线程<br /><ul><li>继承带来的隐患</li></li></ul><li>问题<br /><ul><li>假设A的构造函数中启动某个线程,该线程读取A中的实例变量i
  14. 14. B继承A,并在构造函数中重新给i赋值
  15. 15. 问题: B的实例初始化,首先初始化父类A,启动线程,线程此时读取的i非B所期望。
  16. 16. 解决:
  17. 17. 1、不允许继承——final
  18. 18. 2、单独的start方法(推荐)</li></li></ul><li>正确使用wait/notify<br /><ul><li>一个世界在等待</li></li></ul><li>常见问题<br /><ul><li>代码:
  19. 19. 问题:
  20. 20. 未同步
  21. 21. If替代while——虚假唤醒
  22. 22. notify替代notifyAll——被遗忘的线程</li></li></ul><li>Atomic+Atomic!=Atomic<br /><ul><li>MethodA is thread-safe
  23. 23. MethodBis thread-safe
  24. 24. 组合起来还是线程安全的吗?
  25. 25. public void methodC(){
  26. 26. MethodA();
  27. 27. MethodB();
  28. 28. }</li></li></ul><li>我要同步<br /><ul><li>容器是同步的,就没有问题了吗?
  29. 29. 同步的不是容器,而是寂寞</li></li></ul><li>正确处理中断<br /><ul><li> 将中断进行到底</li></li></ul><li>Thread.interrupt()干什么了?<br /><ul><li>设置中断状态
  30. 30. 中断阻塞操作</li></li></ul><li>How<br /><ul><li>wait,sleep,join都是nativemthod,直接抛出InterruptedException
  31. 31. InterruptibleChannel,关闭连接,
  32. 32. 抛出ClosedByInterruptException(AbstractInterruptibleChannel.java):</li></li></ul><li>错误案例1<br /><ul><li>取消任务,取消不了?</li></li></ul><li>错误案例2<br /><ul><li>吞掉中断,上层代码怎么办?</li></li></ul><li>错误案例3<br /><ul><li>包装成Runtime异常?</li></li></ul><li>取消任务的正确做法<br /><ul><li>没有阻塞操作, volatile状态变量
  33. 33. 不响应中断的阻塞操作,如socket.read()之类,关闭socket。
  34. 34. 响应中断的阻塞操作(如sleep,wait,join等),推荐状态变量+捕捉中断异常</li></li></ul><li>处理InterruptedException<br /><ul><li>除非你明确知道你在干什么,否则不要简单地catch并忽略
  35. 35. 声明Check异常,继续抛出,交给他人处理
  36. 36. 重设中断状态,让上层代码发现中断状态。</li></li></ul><li>嫁错郎,加错锁<br /><ul><li>女怕嫁错郎</li></li></ul><li>Don't<br /><ul><li>#1: Don’t synchronize on an object you’re changing
  37. 37. #2: Don’t synchronize on a String literal
  38. 38. #3: Don’t synchronize on auto-boxed values
  39. 39. #4: Don’t synchronize on null
  40. 40. #5: Don’t synchronize on a Lock object
  41. 41. #6: Don’t synchronize on getClass()
  42. 42. #7: Be careful locking on a thread-safe object with
  43. 43. encapsulated locking </li></li></ul><li>错误案例1<br /><ul><li>只把杭州当汴州
  44. 44. 问题:
  45. 45. foo是null?
  46. 46. 改变了foo指向的对象,加锁形同虚设</li></li></ul><li>错误案例2<br /><ul><li>躲猫猫
  47. 47. 问题:
  48. 48. Bar跟Foo的test方法中的锁不一样。</li></li></ul><li>错误案例3<br /><ul><li>对同步的容器加锁,使用的锁与容器内部使用的锁不一定是同一个。
  49. 49. HashTable、Vector√
  50. 50. ConcurrentHashMap X</li></li></ul><li>不一致的同步<br /><ul><li>int的读写,问题不是太大</li></li></ul><li>邂逅Map<br /><ul><li>忘记我的另一半,不可原谅</li></li></ul><li>阿甘,先别run<br /><ul><li>美国犀利哥</li></li></ul><li>Start Vs. Run<br /><ul><li>哥只是爱跑步
  51. 51. 问题:
  52. 52. http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4533087
  53. 53. 在jdk1.4.2以前,每个thread初始化都会加入ThreadGroup,如果你只是调用run而不是
  54. 54. start,那么此thread将无法被正常回收造成内存泄漏。在JDK5之后,将加入ThreadGroup
  55. 55. 这一步从构造函数转移到start方法,因此不会有问题。</li></li></ul><li>正确使用Volatile<br /><ul><li>Volatile能做什么?
  56. 56. 状态标识,如取消任务线程
  57. 57. 安全发布,如修复DLC问题
  58. 58. 开销较低的读写锁</li></li></ul><li>正确使用Volatile <br /><ul><li>Volatile不能做什么?
  59. 59. 不能用于做计数器
  60. 60. 与其他变量构成不变式</li></li></ul><li>规避j.u.c的bug<br /><ul><li>LinkedBlockingQueue.poll(time,timeUnit) 内存泄漏,其他queue也有类似问题
  61. 61. http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6460501
  62. 62. Semaphore.tryAcquire内存泄漏甚至hang住
  63. 63. http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6460501
  64. 64. http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6241823
  65. 65. ReentrantReadWriteLock可能在没有任何线程持有锁的情况下被hang住,这是
  66. 66. 一系列的BUG:
  67. 67. http://bugs.sun.com/view_bug.do?bug_id=6822370
  68. 68. http://bugs.sun.com/view_bug.do?bug_id=6903249
  69. 69. 可以说j.u.c在1.5这个版本上有非常多的bug,具体可以查看下sun的bug database
  70. 70. ,推荐升级jdk到最新稳定版。</li></li></ul><li>End,Thanks <br />

×