Java实现“睡排序”——线程池Executors的使用

前提

之前在知乎上看见一个有意思的排序算法——睡排序。

睡排序最早好像是4chan上一个用户用shell脚本实现的:

算法思想简洁明了:利用进程的sleep来实现 越大的数字越迟输出。

虽然像2L说的那样,这个算法没什么利用价值。但我打算试着用Java来实现一下这个“睡排序”。

Java实现

既然选择用Java来实现,我们就没必要为数组中每一个元素启一个进程,启多线程就够了。

Java启线程的方式有很多:1.继承Thread 2.实现Runnable 3.实现Callable ...

而当前需求是要同时启固定数量的多个线程,那么通过Executor提供的线程池来启线程最合适不过了。

下面是代码:

    public static List<Integer> sleepSort(int[] nums){
        List<Integer> res = new Vector<>();  // 1
        ExecutorService executor = Executors.newFixedThreadPool(nums.length);
        IntStream.of(nums).forEach(i -> executor.execute(() -> {
            try {
                Thread.sleep(i * 200);  // 2
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            res.add(i);
        }));
        executor.shutdown();
        while (true){  // 3
            if (executor.isTerminated())
                break;
        }
        return res;
    }

几个注意点:

1. 作为一个排序方法只是打印结果的话未免有些单调,所以我打算返回一个List作为排序后的结果。因为涉及多线程的操作,这里选择线程安全的Vector。

2. forEach的遍历方式会使各个线程的启动时间有细微的差距,因此sleep的时间不能太多。经过测试,* 200(ms) 比较合适。

3. 在返回结果之前,必须保证所有线程执行完毕。注意 "executor.shutdown()" 只是关闭线程池,并不会终止线程。所以要通过 "executor.isTerminated()" 来判断。

算法分析
这个算法看起来的复杂度是O(nums.length)。 实际上,复杂度为O(n ^ 2 ),因为维护多个后台线程程依赖于CPU来管理进程的上下文切换和优先级,所以该算法基本上将实际排序外包给了CPU。

测试

写一个生成乱序数组的方法,用于生成测试用例。

    public static int[] getRandomArray(int len, int maxNum){
        int[] res = new int[len];
        Random random = new Random();
        for (int i=0;i<len;i++) {
            res[i] = random.nextInt(maxNum);
        }
        return res;
    }

    public static void main(String[] args){
        System.out.println(sleepSort(getRandomArray(18, 29)));
    }

结果:

理论上数组的最大长度为当前JVM能创建的最大线程数:(系统CPU内存- JVM内存- 系统预留内存) / (线程栈的大小)

最后

该算法仅供娱乐,如何什么可以改进的地方,欢迎讨论。

原文地址:https://www.cnblogs.com/yucfeng/p/9204677.html