Java线程与Map深度解析:Thread.sleep() vs wait(), ThreadLocal内存泄露及HashMap...

需积分: 2 0 下载量 42 浏览量 更新于2024-08-05 收藏 5KB MD 举报
"这篇学习小结主要探讨了Java编程中关于Thread和Map的两个核心知识点:Thread的sleep()和wait()方法的区别,以及ThreadLocal的作用和内存泄漏问题,并深入讲解了HashMap与ConcurrentHashMap的线程安全性差异。" ### 1. sleep()与wait()的区别 在Java中,`Thread.sleep()` 和 `Object.wait()` 是两个不同的概念: - **sleep()**: 这是 `Thread` 类的一个静态方法,当调用后,当前线程将暂停执行指定的时间,然后继续执行,但不会释放任何锁。这意味着其他线程即使获得了CPU时间片,也无法立即访问被当前线程持有的锁。 - **wait()**: 这是 `Object` 类的一个方法,调用前需要获取到对象的锁。执行后,线程不仅会暂停执行,还会释放所持有的锁,进入等待状态。线程的唤醒需要其他线程调用同一对象的 `notify()` 或 `notifyAll()` 方法。唤醒后,线程需要再次竞争CPU资源才能继续执行。 ### 2. ThreadLocal(TL) **ThreadLocal** 提供了一个线程局部变量,每个线程都有自己的副本,互不影响。在内部,每个线程都有一个 `ThreadLocalMap`,存储了键值对,键是 `ThreadLocal` 对象,值是用户设置的值。由于使用弱引用作为键,弱引用的特性使得当 `ThreadLocal` 没有外部强引用时,可以在垃圾收集期间被回收。然而,这可能导致内存泄漏,因为弱引用的 `ThreadLocal` 被回收后,对应的 `ThreadLocalMap` 中的值却仍然存在,形成一条强引用链:`ThreadRef -> Thread -> ThreadLocalMap -> Entry -> value`。为防止这种情况,应当在不再使用 `ThreadLocal` 时调用 `remove()` 方法。 ### 3. HashMap与ConcurrentHashMap **HashMap** 在JDK 1.7及更早版本中不是线程安全的,因为它在多线程环境下可能出现数据一致性问题。例如,当多个线程同时进行扩容操作时,可能会导致环形链表的形成,进而引发死循环,或者在某些情况下丢失插入的数据。 **ConcurrentHashMap** 是线程安全的哈希映射,它通过分段锁机制实现了高效的并发性能。相比于同步的 `Hashtable` 或者在多线程环境下使用 `synchronized` 关键字对 `HashMap` 进行同步,`ConcurrentHashMap` 提供了更好的扩展性和性能。 ### 3.1 HashMap线程不安全的原因 在JDK 1.7的实现中,`HashMap` 扩容时会使用头插法,即移动节点到新的桶位置时,会将其插入到链表的头部。当多个线程同时进行扩容,就可能导致元素被错误地插入到错误的位置,形成环形链表。此外,因为扩容过程涉及到多个步骤,如计算新索引、创建新链表、迁移节点等,如果这些步骤在并发执行时交错,可能会导致某些节点丢失,破坏数据一致性。 总结,理解并正确使用 `Thread.sleep()`、`wait()`、`ThreadLocal` 以及 `HashMap` 和 `ConcurrentHashMap` 的特性,对于编写高效且线程安全的Java程序至关重要。在多线程环境中,选择合适的数据结构和同步策略能有效避免潜在的并发问题。