Java如何实现代码块无中断执行?多线程网络爬虫并发问题求解
问题根因
next()方法存在竞态条件:你对toVisit.size()的判断放在了同步块之外,可能出现判断时队列还有元素,进入同步块前元素被其他线程取走,最终调用get(0)时抛出索引越界异常。- 内部状态泄露:
getToVisit()和getVisited()直接返回了内部集合的引用,即使方法加了同步锁,外部拿到引用后仍然可以随意修改集合内容,直接破坏线程安全。 - 你提到的"让代码块不被其他线程打断"的需求,只要把所有访问共享可变变量的操作都放在同一个对象锁的
synchronized同步块中,就可以保证同一时间只有一个线程执行该代码块,不会出现多线程干扰的问题,你之前的问题是部分共享变量操作漏在了同步块外,并不是同步逻辑本身无效。
修复方案
1. 修复UrlHandler类的线程安全问题
import java.util.*; import java.util.concurrent.ThreadLocalRandom; public class UrlHandler { // 移除volatile,引用本身不会变更,用final保证安全 private final Set<String> visited = new HashSet<>(); private final List<String> toVisit = new ArrayList<>(); public void addToVisit(String url) { synchronized (this){ if (!visited.contains(url)) toVisit.add(url); // 新增元素后唤醒等待的取数据线程 this.notifyAll(); } } public void addToVisit(Collection<String> urls) { synchronized (this){ for (String url : urls) if (!visited.contains(url)) toVisit.add(url); this.notifyAll(); } } public void addVisited(String url){ synchronized (this){ visited.add(url); } } public void addVisited(Collection<String> urls){ synchronized (this){ visited.addAll(urls); } } public String next() { synchronized (this){ // 把判空逻辑移入同步块,用wait代替sleep,空等时释放锁不占资源 while (toVisit.isEmpty()) { try { this.wait(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } } return toVisit.remove(0); } } public String nextRandom() { synchronized (this){ // 补充空队列等待逻辑,和next逻辑对齐 while (toVisit.isEmpty()) { try { this.wait(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } } int n = 0; if (toVisit.size() > 1){ n = ThreadLocalRandom.current().nextInt(toVisit.size()); } return toVisit.remove(n); } } public List<String> getToVisit() { synchronized (this){ // 返回集合副本,不暴露内部引用 return new ArrayList<>(toVisit); } } public Set<String> getVisited() { synchronized (this){ // 返回集合副本,不暴露内部引用 return new HashSet<>(visited); } } }
2. 测试用例适配
你现有的测试用例不需要改动,修复后的UrlHandler可以直接通过测试。如果后续有动态添加待爬URL的场景,建议根据实际任务数量提交线程,避免提前结束测试。
内容的提问来源于stack exchange,提问作者Andrea Zasa
相关产品推荐
相关产品推荐

