线程池的使用

一、简述

在开发中,频繁的创建和销毁一个线程,是极耗资源的,为此创建一个可重用指定线程数的线程池,以共享的无界队列方式来运行这些线程,可以有效的规划线程的使用。
线程池顾名思义,也就是线程的集合,在java中大致有这几种线程池:

  •     newSingleThreadExecutor  创建一个单线程化的线程池, 它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行,可以控制线程的执行顺序
  •     newFixedThreadPool  创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待,当创建的线程池数量为1的时候。也类似于单线程化的线程池,当为1的时候,也可控制线程的执行顺序
  •     newCachedThreadPool  创建一个可缓存线程池,如果线程池长度超过需要的线程数量,可灵活回收空闲线程,若无可回收,则新建线程
  •     newScheduledThreadPool  创建一个定长线程池,支持定时及周期性任务执行,可以作一个定时器使用


    
二、用法案例

说太多原理,讲太多种方式,还没让人明白怎么用也是瞎BB。下面直接通过完整代码讲述newFixedThreadPool方式创建一个定长线程池的用法,然后再简述下其它用法和原理就了解了。
代码:

public class ThreadPoolTest {

	public static void main(String[] args) {
		try {
			//调用测试线程池的使用
			ThreadPoolTest threadPoolTest = new ThreadPoolTest();
			threadPoolTest.callService();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

	}
	
	/**
	 * 假设有8条数据需要分别放置在不同的任务线程中处理
	 * @throws InterruptedException
	 */
	public void callService() throws InterruptedException {
		//创建一个定长为3的线程池
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
        
        for (int i = 1; i <= 8; i++){
        	Thread.sleep(100);//稍作等待
        	System.out.println("-------第"+i+"次for循环  -------");
        	
            int sNum = i;
            fixedThreadPool.execute(new Runnable() {//线程池开启线程任务
                public void run() {
                	SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
            		
            		System.out.println("现在是线程\""+Thread.currentThread().getName()+"\",他在操作第"+sNum+"条数据----开始:"+sdf.format(new Date()));
            		
            		try { 
            			Thread.sleep(5000);//假设,处理一条数据需要5秒
					} catch (InterruptedException e) { e.printStackTrace(); }
            		
        			System.out.println("现在是线程\""+Thread.currentThread().getName()+"\",他在操作第"+sNum+"条数据----结束:"+sdf.format(new Date()));
                }
            });
        }
        
        System.out.println("-------方法结束 -------");  
	}

}


输出:

结论1:由上面的案例(创建一个定长为3的线程池,循环执行8个任务)的输出可以看到:
任务加入到线程池后,线程池即启动线程执行加入的任务,当启动线程数达到线程池的上限(这里为3)后,剩余的任务会加入到线程池的等待队列中,然后主程序继续向下执行至"方法结束",而等待队列中的任务会在线程池线程空闲后被依次执行;所有任务结束后,线程池并不会被关闭。

引申改进:
    在开发过程中,我们常常需要等待所有线程任务执行完毕后再返回,这里我们通过shutdown()isTerminated()两方法,联合实现。
代码:

public void callService() throws InterruptedException {
		//创建一个定长为3的线程池
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
        
        for (int i = 1; i <= 8; i++){
        	Thread.sleep(100);//稍作等待
        	System.out.println("-------第"+i+"次for循环  -------");
        	
            int sNum = i;
            fixedThreadPool.execute(new Runnable() {//线程池开启线程任务
                public void run() {
                	SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
            		
            		System.out.println("现在是线程\""+Thread.currentThread().getName()+"\",他在操作第"+sNum+"条数据----开始:"+sdf.format(new Date()));
            		
            		try { 
            			Thread.sleep(5000);//假设,处理一条数据需要5秒
					} catch (InterruptedException e) { e.printStackTrace(); }
            		
        			System.out.println("现在是线程\""+Thread.currentThread().getName()+"\",他在操作第"+sNum+"条数据----结束:"+sdf.format(new Date()));
                }
            });
        }
        
        fixedThreadPool.shutdown();//关闭线程池,不再接收新任务,如果队列中还有任务,则等待执行完毕
        while (true) {  
            if (fixedThreadPool.isTerminated()) {//关闭后所有任务都已完成,则返回 true
                System.out.println("-------线程池执行完毕 -------");  
                break;
            }  
            Thread.sleep(200);//为了避免无限制的循环判断,让主线程睡眠200毫秒后判断一次(因此会造成所有任务执行完毕后,200毫秒后才得到通知,设置时间越小,误差越小)
        } 
        
        System.out.println("-------方法结束 -------");  
	}


输出:

 

结论二:额......没啥了,看注解、看输出,还不能理解就凉凉了。线程池的一些方法可以看《线程池ExecutorService的主要方法》了解下。

 

三、线程的四种用法

刚详细的讲述了一种线程池的用法,想必对线程池有一个较深的印象了,下面再回过头简单介绍下"简述"中提到的4种用法,上代码看注释吧:

public class ThreadPoolFourTest {

    /**
     * newSingleThreadExecutor 创建一个单线程化的线程池,
     * 它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
     */
    public static void threadPool_test_1(){
        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
        for(int i = 1; i <= 8; i++){
            final int index = i;
            singleThreadExecutor.execute(new Runnable() {
            	
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"执行了第"+index+"个任务");
                }
            });
        }
    }
    
    
    /**
     * newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
     */
    public static void threadPool_test_2(){
        //当参数为1的时候,可以控制线程的执行顺序
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
        for(int i = 1; i <= 8; i++){
            final int index = i;
            fixedThreadPool.execute(new Runnable() {
            	
                @Override
                public void run() {
                	System.out.println(Thread.currentThread().getName()+"执行第"+index+"个任务");
                	try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace();}
                }
            });
        }
    }
    
    
    /**
     * newCachedThreadPool创建一个可缓存线程池,
     * 如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
     */
    public static void threadPool_test_3(){
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        for (int i = 0; i <= 8; i++) {
        	final int index = i;  
            cachedThreadPool.execute(new Runnable() {
                
                @Override
                public void run() {
                	System.out.println(Thread.currentThread().getName()+"执行第"+index+"个任务");
                }
            });
        }
    }
    
    
	/**
     * 测试newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
     * 一般可做定时器使用
     */
    public static void threadPool_test_4(){
    	System.out.println("开始设置newScheduledThreadPool启动任务啦:"+new Date());
    	
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
        /**
         * 第2个参数:首次执行该线程的延迟时间,之后失效
         * 第3个参数:首次执行完之后,再过该段时间再次执行该线程,具有周期性
         * 第4个参数:时间单位
         */
        scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            
            @Override
            public void run() {
            	System.out.println("执行了newScheduledThreadPool任务:"+new Date());
                
            }
        }, 2, 3, TimeUnit.SECONDS);
        
    }
	
}


结束:......好了,用法就到这里了,上面的连接池都是由线程工具类Executors来创建的,newFixedThreadPool、newCachedThreadPool什么的都是 new ThreadPoolExecutor出来的,只是参数不一样,还有源码addWorker()控制线程数啥的,有兴趣的可以自己了解下,或者关注下,后面整理了发出来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值