记得之前有一家公司的面试问随机数排序算法,有一个大佬玩的花招,他用多线程的睡眠机制,去开辟多线程同时睡眠,随机数小的就会先唤醒打印,这样就实现了排序,如果降序也很简单,只需要将先唤醒的存到数组的最后面,将指针前移。
那么我就诞生一个想法,这种机制可以用到计时任务中去,当然前提是任务很少,或者说就只适合学生做项目用,不适合生产,玩着来呗。
来一起看看:
任务信息实体:
@Data
public class TaskInfo {
private String TaskServerName;
private Integer TaskTime;
private TaskType Type = TaskType.TASK_DELETE_CACHE;
public TaskInfo(String serverName, Integer taskTime, TaskType type){
this.TaskServerName = serverName;
this.TaskTime = taskTime;
this.Type = type;
}
public TaskInfo(){
}
}
任务结束后是否留缓存或者数据库枚举
public enum TaskType {
/*计时结束即删除、计时结束不删除仅仅修改时间、计时结束不删除进入数据库*/
TASK_DELETE_CACHE,TASK_INTER_CACHE,TASK_SUPER_CACHE
}
计时任务存储的数据结构
public class TimerHashMap implements Map<String,TaskInfo>{
/*单例模式下堆内存只有一个静态的baseData极其容易在多线程环境下出现异常,上一把对象锁*/
private final static HashMap<String, TaskInfo> baseData = new HashMap<>(20);
public boolean endTaskInCache(String key,TaskInfo taskInfo){
synchronized (baseData){
if (baseData.containsKey(key)){
boolean remove = baseData.remove(key,taskInfo);
if (remove){
taskInfo.setTaskTime(0);
TaskInfo put = baseData.put(key, taskInfo);
return put == null;
}
return false;
}
return false;
}
}
@Override
public int size() {
return baseData.size();
}
@Override
public boolean isEmpty() {
return baseData.isEmpty();
}
@Override
public boolean containsKey(Object key) {
return baseData.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return baseData.containsValue(value);
}
@Override
public TaskInfo get(Object key) {
synchronized (baseData) {
return baseData.get(key);
}
}
@Nullable
@Override
public TaskInfo put(String key, TaskInfo value) {
synchronized (baseData){
return baseData.put(key,value);
}
}
@Override
public TaskInfo remove(Object key) {
synchronized (baseData){
return baseData.remove(key);
}
}
@Override
public void putAll(@NotNull Map<? extends String, ? extends TaskInfo> m) {
synchronized (baseData){
baseData.putAll(m);
}
}
@Override
public void clear() {
synchronized (baseData){
baseData.clear();
}
}
@NotNull
@Override
public Set<String> keySet() {
synchronized (baseData){
return baseData.keySet();
}
}
@NotNull
@Override
public Collection<TaskInfo> values() {
synchronized (baseData){
return baseData.values();
}
}
@NotNull
@Override
public Set<Entry<String, TaskInfo>> entrySet() {
synchronized (baseData){
return baseData.entrySet();
}
}
}
计时任务注册与运行组件
/**
* 计时任务(依赖与hashmap数据结构,结合线程池中特定线程的睡眠实现计时功能)
*/
@Component
public class TimerTask {
@Resource
private TimerHashMap timer;
@Resource
private ThreadPoolTaskExecutor userThreadPoolTaskExecutor;
/**
* 计时任务的注册与任务完毕后的销毁以及存留
* @param serverKey 计时任务标识字段
* @param taskInfo 计时任务信息
*/
public void setTimer(String serverKey, TaskInfo taskInfo) {
boolean sign = timer.containsKey(serverKey);
if (sign)
return;
try {
userThreadPoolTaskExecutor.execute(
new Thread(
() -> {
TaskInfo put = timer.put(serverKey, taskInfo);
if (put == null) {
try {
System.out.println(Thread.currentThread().getName()+serverKey+"计时开始------>");
Thread.sleep(taskInfo.getTaskTime());
if (taskInfo.getType() == TaskType.TASK_DELETE_CACHE) {
TaskInfo remove = timer.remove(serverKey);
if (remove == null)
throw new Exception("计时任务删除失败");
}else if (taskInfo.getType()== TaskType.TASK_INTER_CACHE){
boolean result = timer.endTaskInCache(serverKey, taskInfo);
if (!result){
throw new Exception("计时任务无法正常结束");
}
}else {
/*将计时任务插入计时任务数据库.....*/
}
System.out.println(Thread.currentThread().getName()+serverKey+"任务结束------>");
} catch (Exception e) {
e.printStackTrace();
}
}
},
serverKey
)
);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 判断计时任务是否结束
* @param serverKey 计时任务标识字段
* @return 是否已经结束
*/
public boolean taskIsEnd(String serverKey){
boolean result = timer.containsKey(serverKey);
if (result){
return timer.get(serverKey).getTaskTime() == 0;
}
return false;
}
}
线程池与计时任务存储数据结构配置在Application中:
@Bean(name = "userThreadPoolTaskExecutor")
public ThreadPoolTaskExecutor getUserThreadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//此方法返回可用处理器的虚拟机的最大数量; 不小于1
//int core = Runtime.getRuntime().availableProcessors();
int core = 1;
//设置核心线程数
executor.setCorePoolSize(core);
//设置最大线程数
executor.setMaxPoolSize(core * 2 + 1);
//除核心线程外的线程存活时间
executor.setKeepAliveSeconds(3);
//如果传入值大于0,底层队列使用的是LinkedBlockingQueue,否则默认使用SynchronousQueue
executor.setQueueCapacity(40);
//线程名称前缀
executor.setThreadNamePrefix("userService-thread-execute");
//设置拒绝策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return executor;
}
/*保证多线程之间共享某些参数从而定义链表图数据结构实现某些数据的共享:可使用java关键字替代*/
@Bean(name = "cache")
public Map<String, String> StringCache() {
return new LinkedHashMap<>();
}
/*注入自定义计时任务采用的数据结构,类似于redis里的数据结构*/
@Bean(name = "timer")
public TimerHashMap TimerCache(){
return new TimerHashMap();
}
这套工具能够实现redis的缓存过期功能,但是不支持过多的线程开销,玩玩可以,别当真。