| | |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.concurrent.CountDownLatch; |
| | | import java.util.concurrent.LinkedBlockingQueue; |
| | | import java.util.concurrent.ThreadPoolExecutor; |
| | | import java.util.concurrent.TimeUnit; |
| | | import java.util.concurrent.*; |
| | | import java.util.concurrent.atomic.AtomicInteger; |
| | | |
| | | public class TrajectoryDensifier { |
| | | |
| | | private static final int THREAD_NUM = 5; |
| | | |
| | | /** |
| | | * 轨迹补全计算线程池(类级别单例复用) |
| | | * 修复:原代码每次调用 densify() 都 new ThreadPoolExecutor 并在方法结束时 shutdown, |
| | | * 频繁创建/销毁线程开销大,且异常路径(latch.await 被中断)会跳过 shutdown 导致线程泄漏。 |
| | | * 改为单例线程池复用,避免上述问题。 |
| | | */ |
| | | private static final ThreadPoolExecutor DENSIFY_EXECUTOR = new ThreadPoolExecutor( |
| | | 5, 5, 60L, TimeUnit.SECONDS, |
| | | new LinkedBlockingQueue<>(256), |
| | | new ThreadFactory() { |
| | | private final AtomicInteger counter = new AtomicInteger(0); |
| | | @Override |
| | | public Thread newThread(Runnable r) { |
| | | Thread t = new Thread(r, "trajectory-densify-" + counter.incrementAndGet()); |
| | | t.setDaemon(true); |
| | | return t; |
| | | } |
| | | }, |
| | | new ThreadPoolExecutor.CallerRunsPolicy() |
| | | ); |
| | | |
| | | static { |
| | | // 允许核心线程超时回收,空闲时不占用资源 |
| | | DENSIFY_EXECUTOR.allowCoreThreadTimeOut(true); |
| | | } |
| | | |
| | | |
| | | public static List<LonLatDto> densify(List<LonLatDto> originalPoints, int distance) { |
| | | if (originalPoints == null || originalPoints.size() <= 1) { |
| | | return new ArrayList<>(originalPoints); |
| | | } |
| | | int num = originalPoints.size() / THREAD_NUM; |
| | | if(num == 0){ |
| | | num = 1; |
| | | } |
| | | ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(num, num, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()); |
| | | CountDownLatch latch = new CountDownLatch(num); |
| | | Map<Integer, List<LonLatDto>> map = new HashMap<>(); |
| | | Map<Integer, List<LonLatDto>> map = new ConcurrentHashMap<>(); |
| | | for (int i = 0; i < num; i++) { |
| | | List<LonLatDto> trackPoints = originalPoints.subList(i * THREAD_NUM, i == num - 1 ? originalPoints.size() : (i + 1) * THREAD_NUM); |
| | | int finalI = i; |
| | | poolExecutor.execute(()->{ |
| | | List<LonLatDto> new_polyline = new ArrayList<>(); |
| | | for (int j = 0; j < trackPoints.size(); j++) { |
| | | LonLatDto trackPoint = trackPoints.get(j); |
| | | if(j == trackPoints.size() - 1){ |
| | | new_polyline.add(new LonLatDto(trackPoint.getLongitude(), trackPoint.getLatitude())); |
| | | break; |
| | | } |
| | | LonLatDto trackPoint1 = trackPoints.get(j+1); |
| | | DENSIFY_EXECUTOR.execute(()->{ |
| | | try { |
| | | List<LonLatDto> new_polyline = new ArrayList<>(); |
| | | for (int j = 0; j < trackPoints.size(); j++) { |
| | | LonLatDto trackPoint = trackPoints.get(j); |
| | | if(j == trackPoints.size() - 1){ |
| | | new_polyline.add(new LonLatDto(trackPoint.getLongitude(), trackPoint.getLatitude())); |
| | | break; |
| | | } |
| | | LonLatDto trackPoint1 = trackPoints.get(j+1); |
| | | |
| | | //对两个点位进行坐标补全 |
| | | List<TrackPoint> track = new ArrayList<>(); |
| | | track.add(new TrackPoint(trackPoint.getLatitude(), trackPoint.getLongitude(), null)); |
| | | track.add(new TrackPoint(trackPoint1.getLatitude(), trackPoint1.getLongitude(), null)); |
| | | //10米补全一个点位 |
| | | List<TrackPoint> densified = densifyByDistance(track, distance); |
| | | for (TrackPoint point : densified) { |
| | | new_polyline.add(new LonLatDto(point.getLongitude(), point.getLatitude())); |
| | | //对两个点位进行坐标补全 |
| | | List<TrackPoint> track = new ArrayList<>(); |
| | | track.add(new TrackPoint(trackPoint.getLatitude(), trackPoint.getLongitude(), null)); |
| | | track.add(new TrackPoint(trackPoint1.getLatitude(), trackPoint1.getLongitude(), null)); |
| | | //10米补全一个点位 |
| | | List<TrackPoint> densified = densifyByDistance(track, distance); |
| | | for (TrackPoint point : densified) { |
| | | new_polyline.add(new LonLatDto(point.getLongitude(), point.getLatitude())); |
| | | } |
| | | } |
| | | if (!new_polyline.isEmpty()) { |
| | | new_polyline.remove(new_polyline.size() - 1); |
| | | } |
| | | map.put(finalI, new_polyline); |
| | | } finally { |
| | | latch.countDown(); |
| | | } |
| | | new_polyline.remove(new_polyline.size() - 1); |
| | | map.put(finalI, new_polyline); |
| | | latch.countDown(); |
| | | }); |
| | | } |
| | | try { |
| | | latch.await(); |
| | | poolExecutor.shutdown(); |
| | | } catch (InterruptedException e) { |
| | | e.printStackTrace(); |
| | | Thread.currentThread().interrupt(); |
| | | throw new RuntimeException(e); |
| | | } |
| | | List<LonLatDto> result = new ArrayList<>(); |
| | | for (int i = 0; i < num; i++) { |
| | | result.addAll(map.get(i)); |
| | | List<LonLatDto> chunk = map.get(i); |
| | | if (null != chunk) { |
| | | result.addAll(chunk); |
| | | } |
| | | } |
| | | //添加最后一个点 |
| | | result.add(originalPoints.get(originalPoints.size() - 1)); |