线程安全类与对象:线程安全类的实例是否必然线程安全?求反例
线程安全类的所有实例是否必然具备线程安全性?
答案很明确:不是。线程安全类只能保证单个方法调用的线程安全性,但如果对实例执行复合操作(多个方法的组合调用),或者在外部未正确同步的场景下使用,依然会出现线程安全问题。
举个最典型的例子,Java中的Vector类——它的所有公共方法都带有synchronized修饰,单个方法调用是线程安全的,但组合操作就不一定了:
// 多个线程共享的Vector实例 Vector<String> sharedList = new Vector<>(); // 线程1执行的代码 if (!sharedList.isEmpty()) { sharedList.remove(0); } // 线程2同时执行的代码 if (!sharedList.isEmpty()) { sharedList.remove(0); }
假设此时sharedList中只有一个元素:
- 线程1调用
isEmpty(),返回false,准备执行删除操作 - 线程2在线程1执行删除前,也调用
isEmpty(),同样返回false - 线程1先执行
remove(0),元素被移除,列表变为空 - 线程2再执行
remove(0),直接抛出ArrayIndexOutOfBoundsException
问题就出在:isEmpty()和remove(0)这两个单独安全的方法,组合起来并不是原子操作——没有任何机制保证这两个步骤之间不会被其他线程打断。
还有另一个常见场景:线程安全集合的迭代操作。比如Hashtable(线程安全的哈希表),它的迭代器是"快速失败"的。如果一个线程在迭代Hashtable的同时,另一个线程修改了集合的结构(添加/删除元素),迭代器会立刻抛出ConcurrentModificationException。这是因为迭代是一系列连续的操作(取元素、移动指针等),这些操作的组合并没有被同步保护,哪怕单个的修改/获取方法是线程安全的。
所以记住:线程安全类的"线程安全"是指单个方法层面的安全,如果你需要执行多步操作的组合,必须自己额外添加同步措施(比如给实例加锁),才能保证整个操作的线程安全性。
内容的提问来源于stack exchange,提问作者Maria
相关产品推荐
相关产品推荐

