不使用锁或同步机制,如何让非原子数组实现线程安全?
1. 能否在不使用锁或synchronized关键字的前提下,让非原子变量实现线程安全?
可以实现,核心是避免多线程对同一变量的并发竞争,常见手段包括:
- 线程封闭:将变量的使用限制在单个线程内,比如用ThreadLocal存储线程私有数据,或仅在单线程中操作变量。
- 分治汇总:把共享任务拆分到多个独立子任务,每个线程维护自己的局部状态,最后统一汇总结果,避免并发修改同一变量。
- 无状态设计:程序不维护共享状态,所有操作仅依赖输入参数,自然不存在线程安全问题。
- volatile关键字:仅适用于单一读/写操作的变量,能保证可见性和有序性,但无法解决自增这类复合操作的原子性问题。
2. JUnit测试代码的解决方案
原代码存在两个核心问题:
- 共享数组元素的
count[0]++是读-改-写复合操作,多线程并发执行会导致更新丢失。 awaitTermination被错误放在循环内,每次仅等待10ms,大部分任务未执行完成就触发断言。
针对要求(异步运行、无锁/同步、不增加超时时间、必须用数组),解决方案采用线程专属数组元素的方式,让每个线程仅操作自己的数组下标,避免竞争,同时修正线程池的等待逻辑:
@Test void arrayCheck() throws InterruptedException { final int[] count = {0}; // 与线程池大小匹配的数组,每个线程操作专属下标 final int[] threadCounts = new int[5]; ExecutorService executorService = Executors.newFixedThreadPool(5); for (int i = 0; i < 10000; i++) { executorService.execute(() -> { try { Thread.sleep(100); // 从线程名称提取在线程池中的下标 String threadName = Thread.currentThread().getName(); int threadIndex = Integer.parseInt(threadName.split("-")[3]) - 1; // 操作专属数组元素,无并发竞争 threadCounts[threadIndex]++; System.out.println(threadName + "----" + threadCounts[threadIndex] + "---"); Thread.sleep(100); } catch (InterruptedException e) { throw new RuntimeException(e); } }); } executorService.shutdown(); // 循环等待,每次使用原超时时间10ms,直到所有任务完成 while (!executorService.awaitTermination(10, TimeUnit.MILLISECONDS)) { // 空循环继续等待 } // 汇总所有线程的计数到目标数组 for (int num : threadCounts) { count[0] += num; } Assertions.assertEquals(10000, count[0]); }
方案说明:
- 线程专属数组元素:利用固定大小线程池的特性,每个线程对应数组的一个下标,仅操作自己的元素,彻底避免并发竞争,无需锁或同步机制。
- 修正等待逻辑:先提交所有任务,关闭线程池后循环调用
awaitTermination(保持原10ms超时时间),直到所有任务执行完毕,确保断言前所有任务都已完成。 - 汇总结果:最后将所有线程的局部计数汇总到目标数组
count中,保证最终结果正确。
内容的提问来源于stack exchange,提问作者aditya sharma
相关产品推荐
相关产品推荐

