JAVA线程TimeUnit和定时任务创建的几种方式

本文详细介绍Java中TimeUnit的使用方法,包括时间单位转换和线程延时。同时,探讨了三种创建定时任务的方式:使用Timer和TimerTask,线程池ScheduledExecutorService,以及自定义线程池实现定时任务,推荐使用线程池方式。最后,讲解了线程池的创建和自定义线程池的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一,TimeUnit的使用

主要作用:1. 时间颗粒度转换 2. 延时

1.时间颗粒度转换

TimeUnit是枚举实例,指定颗粒度单位

常用的颗粒度
		TimeUnit.DAYS;         //天  
		Calendar.HOUR_OF_DAY   //小时  是24小时制    
		Calendar.HOU 		   //小时  是12小时制
		TimeUnit.MINUTES       //分钟  
		TimeUnit.SECONDS       //秒  
		TimeUnit.MILLISECONDS  //毫秒 
		
转换:
System.out.println( TimeUnit.DAYS.toHours( 1 ) ); //将1天转化为小时
System.out.println( TimeUnit.HOURS.toSeconds( 1 ));//将1小时转化为秒,转换其他颗粒方法类似

2.延时

TimeUnit提供了可读性更好的线程暂停操作,通常用来替换Thread.sleep()。
记住TimeUnit.sleep()内部调用的Thread.sleep()也会抛出InterruptException。

TimeUnit写法:线程休眠4分钟

public static void main(String[] args) throws InterruptedException {
		TimeUnit.MINUTES.sleep(4);	
}

二,定时任务创建的几种方式

参考博客:Java并发学习之定时任务的几种玩法

不推荐使用 Thread.sleep的方式做定时任务
利用jdk实现定时任务,考虑Timer和ScheduledExecutorService
利用到了Spring,框架自带的定时任务

1.定时任务Timer和TimerTask方式

//TimerTask是一个抽象类,实现了Runnable接口,所以具备了多线程的能力。
TimerTask timerTask = new TimerTask() {
	@Override
	public void run() {
		System.out.println("timerTask 定时执行!");
	}
};

Timer timer = new Timer();
timer.schedule(timerTask, 5000);// 5秒后启动任务  
timer.schedule(timerTask, 5000, 3000);// 5秒后启动任务,以后每隔3秒执行一次线程   

Date date = new Date();
timer.schedule(timerTask, date, 3000);//以date为参数,指定某个时间点执行线程  

2.线程池方式实现(推荐使用)

// 获取线程池
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);

// 延迟3s后,执行定时任务
//		scheduledExecutorService.schedule(new Runnable() {
//			@Override
//			public void run() {
//				System.out.println("scheduledExecutorService 定时执行!");
//			}
//		}, 3, TimeUnit.SECONDS);

// 3s后,首次执行,然后每个3s执行一次
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
	@Override
	public void run() {
		System.out.println("scheduledExecutorService 定时执行!");
	}
}, 3, 3, TimeUnit.SECONDS);

3.线程池方式计算某个时间执行

public class Testb {

	static ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2);
	static class Task extends TimerTask {
        @Override
        public void run() {
            System.out.println("进行校验");
            // 执行完毕,等待到6点发送报警
            int min = Calendar.getInstance().get(Calendar.MINUTE);
            int sec = Calendar.getInstance().get(Calendar.SECOND);
            long delayTime = TimeUnit.MINUTES.toSeconds(59 - min) + (60 - sec);
            executorService.schedule(new Runnable() {
				@Override
				public void run() {
					System.out.println("报警!");
				}
			}, delayTime, TimeUnit.SECONDS);
        }
    }
	public static void main(String[] args) throws InterruptedException {
		//假设每天凌晨5点数据导入完毕
		//在5:15分进行校验, 校验完成之后,45分钟后即六点将校验结果通知给owne
	 	Calendar calendar = Calendar.getInstance();// 15:20  40s
        // 计算sleep的小时数   先计算到5点整
        int sleepHour = 4 - calendar.get(Calendar.HOUR_OF_DAY);//4 - 15 = -11
        if (sleepHour < 0) {
			sleepHour = 24 + sleepHour;
		}
    	int minute = 59 - calendar.get(Calendar.MINUTE);//20
    	int second = 60 - calendar.get(Calendar.SECOND);//40s
        long delay = TimeUnit.HOURS.toSeconds(sleepHour) + TimeUnit.MINUTES.toSeconds(minute) + second;
        // 再加15分钟 延时到5:15
        delay += TimeUnit.MINUTES.toSeconds(15);
        executorService.scheduleAtFixedRate(new Task(), delay, TimeUnit.DAYS.toSeconds(1), TimeUnit.SECONDS);
	}
	
}

三,线程池的创建和自定义线程池

1.线程池的创建

//newFixedThreadPool()固定大小的线程池,阻塞队列使用LinkedBlockingQueue
//newSingleThreadExecutor()单个线程线程池,阻塞队列使用LinkedBlockingQueue
//newCachedThreadPool()缓存线程池,阻塞队列使用SynchronousQueue
//newScheduledThreadPool定时线程池,该线程池可用于周期性地去执行任务,通常用于周期性的同步数据。

//采用Executors创建定时线程池
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);

2.自定义线程池

参考博客:JAVA进阶----ThreadPoolExecutor机制

线程池的正确使用:
阿里编码规范:
线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors各个方法的弊端:
1)newFixedThreadPool和newSingleThreadExecutor:
  主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
2)newCachedThreadPool和newScheduledThreadPool:
  主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。

//阻塞队列数
//ArrayBlockingQueue 数组结构组成的有界阻塞队列,此队列按照先进先出(FIFO)的原则
//LinkedBlockingQueue 一个由链表结构组成的无界阻塞队列,此队列按照先出先进的原则进行排序
//SynchronousQueue 不存储元素的阻塞队列

定制属于自己的阻塞线程池:

public class CustomThreadPoolExecutor {    
    /**
     * 自定义线程池
     */
    private ThreadPoolExecutor pool = null;    
        
    /**  
     * 
     * 线程池初始化方法    
     * corePoolSize 核心线程池大小----1  
     * maximumPoolSize 最大线程池大小----3  
     * keepAliveTime 线程池中超过corePoolSize数目的空闲线程最大存活时间----30+单位TimeUnit  
     * TimeUnit keepAliveTime时间单位----TimeUnit.MINUTES  
     * workQueue 阻塞队列----new ArrayBlockingQueue<Runnable>(5)====5容量的阻塞队列  
     * threadFactory 新建线程工厂----new CustomThreadFactory()====定制的线程工厂  
     * rejectedExecutionHandler 当提交任务数超过maxmumPoolSize+workQueue之和时,  
     *                          即当提交第41个任务时(前面线程都没有执行完,此测试方法中用sleep(100)),  
     *                          任务会交给RejectedExecutionHandler来处理  
     */    
    public void init() {    
        pool = new ThreadPoolExecutor(    
                1,    
                3,    
                30,    
                TimeUnit.MINUTES,    
                new ArrayBlockingQueue<Runnable>(5),    
                new CustomThreadFactory(),    
                new CustomRejectedExecutionHandler());    
    }    
    
    /**
     * 线程池销毁方法     
     */
    public void destory() {    
        if(pool != null) {    
            pool.shutdownNow();    
        }    
    }    
        
    /**
     * 获取自定义线程池     
     * @return
     */
    public ExecutorService getCustomThreadPoolExecutor() {    
        return this.pool;    
    }    
    
    /**
     * 
     * @Description: 定制的线程工厂
     * @author: lys
     */
    private class CustomThreadFactory implements ThreadFactory {  
    	//Atomic包下提供具有原子性类型:如
    	//AtomicBoolean :原子更新布尔类型。
    	//AtomicLong :原子更新长整型。
    	//AtomicInteger :原子更新整型。 
        private AtomicInteger count = new AtomicInteger(0);    
            
        @Override    
        public Thread newThread(Runnable r) {    
            Thread t = new Thread(r);    
            //addandget为先添加后获取,类似于++i
            String threadName = CustomThreadPoolExecutor.class.getSimpleName() + count.addAndGet(1);    
            System.out.println(threadName);    
            t.setName(threadName);    
            return t;    
        }    
    }    
        
    /**
     *   
     * @Description: 拒绝策略
     * @author: lys
     */
    private class CustomRejectedExecutionHandler implements RejectedExecutionHandler {    
    
        @Override    
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {    
            try {  
                // 核心改造点,由blockingqueue的offer改成put阻塞方法  
            	// offer方法当队列满,而且放入时间超过设定时间时,返回false;
            	// put方法当队列满时,会调用wait方法,put方法会等待一个空的位置出来,然后再执行insert
                executor.getQueue().put(r);  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }  
        }    
    }    
        
    // 测试构造的线程池    
    public static void main(String[] args) {    
          
        CustomThreadPoolExecutor exec = new CustomThreadPoolExecutor();    
        // 1.初始化    
        exec.init();    
        // 获取自定义线程池    
        ExecutorService pool = exec.getCustomThreadPoolExecutor();    
        for(int i=1; i<100; i++) {    
            System.out.println("提交第" + i + "个任务!");    
            // 在线程池里创建线程执行任务	 
            pool.execute(new Runnable() {    
                @Override    
                public void run() {    
                    try {    
                        System.out.println(">>>task is running=====");   
                        TimeUnit.SECONDS.sleep(10);  
                    } catch (InterruptedException e) {    
                        e.printStackTrace();    
                    }    
                }    
            });    
        }    
            
            
        // 2.销毁----此处不能销毁,因为任务没有提交执行完,如果销毁线程池,任务也就无法执行了    
        // exec.destory();    
            
        try {    
            Thread.sleep(10000);    
        } catch (InterruptedException e) {    
            e.printStackTrace();    
        }    
    }    
}   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值