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内存- 系统预留内存) / (线程栈的大小)
最后
该算法仅供娱乐,如何什么可以改进的地方,欢迎讨论。
Java实现“睡排序”——线程池Executors的使用的更多相关文章
- 深入理解Java自带的线程池和缓冲队列
前言 线程池是什么 线程池的概念是初始化线程池时在池中创建空闲的线程,一但有工作任务,可直接使用线程池中的线程进行执行工作任务,任务执行完成后又返回线程池中成为空闲线程.使用线程池可以减少线程的创建和 ...
- 【转】线程池体系介绍及从阿里Java开发手册学习线程池的正确创建方法
jdk1.7中java.util.concurrent.Executor线程池体系介绍 java.util.concurrent.Executor : 负责线程的使用与调度的根接口 |–Execut ...
- java多线程9:线程池
线程池 线程池的优点 我们知道线程的创建和上下文的切换也是需要消耗CPU资源的,所以在多线程任务下,使用线程池的优点就有: 第一:降低资源消耗.通过重复利用已创建的线程降低线程创建和销毁造成的消耗. ...
- Java并发编程:线程池的使用
Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...
- Java多线程系列--“JUC线程池”06之 Callable和Future
概要 本章介绍线程池中的Callable和Future.Callable 和 Future 简介示例和源码分析(基于JDK1.7.0_40) 转载请注明出处:http://www.cnblogs.co ...
- Java多线程系列--“JUC线程池”03之 线程池原理(二)
概要 在前面一章"Java多线程系列--“JUC线程池”02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包括:线程池示例参考代 ...
- JAVA基础拾遗-论线程池的线程粒度划分与深浅放置
摘要:多线程任务处理对提高性能很有帮助,在Java中提供的线程池也方便了对多线程任务的实现.使用它很简单,而如果进行了不正确的使用,那么代码将陷入一团乱麻.因此如何正确地使用它,如以下分享,这个技能你 ...
- Java并发编程:线程池的使用(转)
Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...
- Java视频扩展知识 线程池的了解
Java视频扩展知识 线程池的了解 1.简单介绍: Jdk1.5之后加入了java.util.concurrent包,这个包中主要介绍java中线程以及线程池的使用.为我们在开发中处理线程的 ...
随机推荐
- 最长公共子序列(LCS)思维导图
- WinAPI: OpenProcess、GetExitCodeProcess、TerminateProcess (测试强制关闭 OICQ)
原文:http://www.cnblogs.com/del/archive/2008/03/10/1098502.html //声明: {返回进程的句柄} OpenProcess( dwDesir ...
- AndroidStudio安装教程
Android studio安装与配置 1.首先下载Android studio安装包,可以从http://www.android-studio.org/ 2.下载好该安装包之后,点击进行安装,依次出 ...
- SQA和系统测试规程
1.SQA计划 (1)目的 本计划是定义Online Judge(在线测评系统)项目的SQA组织,SQA任务和职责,项目过程中应遵循的流程.规范和约定等,指导SQA人员进行评审和审计活动,验证项目的产 ...
- 使用spring aop遇到的坑
1.aop 切点配置无误但只对控制器无效. 检测你的aop开启配置是否放在了spring配置文件中,如果是请把它移到mvc配置文件中. 我们知道当spring项目使用了spring mvc时,项目是存 ...
- Java任务调度框架Quartz入门
Quartz[kwɔːts]:石英,其框架和名字一样简单朴素又不失魅力,在Java程序界,Quartz大名鼎鼎,很多Java应用几乎都集成或构建了一个定时任务调度系统,Quartz是一个定时任务调度框 ...
- C# 两个结构相同的类进行赋值
假入两个类A类和B类不有任何继承关系,它们属性名相同,怎么给他们互相赋值呢?下面的方法就可以了. public L SetProperties<T, L>(T t) where L : n ...
- SQL-SERVER学习(一) 数据表的基本操作
1.创建一个数据库,数据库的名字是UserInfo create database UserInfo; 2.创建一个数据表,数据表的名字是UserInfocreate table UserInfo( ...
- 请求是如何传递给StandardEngine的?
将请求的传递过程分解学习. CoyoteAdapter将中持有Connector的引用,所以在Coyote这个类中Connector查找它所属的StandardService,而StandardSer ...
- 哈哈,原来IOC容器的bean是存在DefaultSingletonBeanRegistry的一个Map类型的属性当中。
经过查看源代码发现IOC容器中的bean实例(不知道是不是所有的bean)是存储在一个DefaultSingletonBeanRegistry类实例的一个Map类型的属性当中. 下面是DefaultS ...