java线程池

在java编程中,多线程是解决并发的重要手段,然而使用多线程同时创建和销毁线程需要消耗资源,无限制的创建线程就会导致资源耗尽,系统崩溃。因此,当服务器内存和CPU不是绝对够用时,采用线程池对多线程进行管理是合理的手段。线程池的好处(引用其他文章):

  1. 降低资源消耗,重复利用现有的线程降低线程的创建和销毁对资源的消耗;

  2. 提高响应速率,当任务到达时,不用再等待线程的创建,利用现有线程即可立即执行任务;

  3. 提高线程的可管理性,线程是稀缺资源,如果无限制的创建线程,不仅会消耗系统资源,而且会降低系统的稳定性,使用线程池可以进行统一的分配、调优和监控。

Java中线程池所有的类位于rt.jar中java.util.concurrent包中,线程池核心类是
ThreadPoolExecutor。下面是该类是使用示例:

package thread;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPool {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        try {
            ThreadPoolExecutor threadPool = 
                    new ThreadPoolExecutor(5, 5, 0, TimeUnit.MILLISECONDS,  new LinkedBlockingQueue<Runnable>());
            Thread thread1 = new Thread(new MyRunnable(),"thread1");
            Thread thread2 = new Thread(new MyRunnable(),"thread2");
            Thread thread3 = new Thread(new MyRunnable(),"thread3");
            Thread thread4 = new Thread(new MyRunnable(),"thread4");
            Thread thread5 = new Thread(new MyRunnable(),"thread5");            
            threadPool.execute(thread1);
            threadPool.execute(thread2);
            threadPool.execute(thread3);
            threadPool.execute(thread4);
            threadPool.execute(thread5);
        } catch (IllegalArgumentException e) {
            // TODO: handle exception
            e.printStackTrace();
        }
    }

    static class MyRunnable implements Runnable{
        @Override
        public void run() {
            // TODO Auto-generated method stub
            try {
                Thread.sleep(2000);
                System.out.println("PP:"+Thread.currentThread().getName()+":ggg");
            } catch (InterruptedException e) {
                // TODO: handle exception
                e.printStackTrace();
            }
        }   
    }   
}
运行结果:
PP:pool-1-thread-2:ggg
PP:pool-1-thread-5:ggg
PP:pool-1-thread-1:ggg
PP:pool-1-thread-3:ggg
PP:pool-1-thread-4:ggg

在上面例子中使用ThreadPoolExecutor创建了可以并发执行5个线程的线程池,ThreadPoolExecutor类是线程池的核心类,继承了AbstractExecutorService类,AbstractExecutorService类实现了ExecutorService接口,在concurrent包中线程池相关的类图:
这里写图片描述

ThreadPoolExecutor类构造函数如下:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
}

构造函数需要5个参数:
corePoolSize:核心线程池大小,当线程池小于corePoolSize时,对于新的任务就会创建新线程执行,当线程池数大小超过corePoolSize时,新提交的任务被放入workQueue中;

maximumPoolSize:最大线程池大小,线程池中并发执行的线程的最大数量,当maximumPoolSize大于corePoolSize时,且workQueue已满时,新的任务就会创建新的线程执行;

keepAliveTime:线程池中超过corePoolSize大小的空闲线程存活时间;

unit:keepAliveTime单位;

workQueue:任务队列。

但是在实际开发中,并不建议使用ThreadPoolExecutor构造函数创建线程池,java给出了一个Executors类,Executors类提供了几个静态方法,用来创建线程池

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
}
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

四种方法:
newFixedThreadPool:是创建固定大小的线程池,核心线程数量与最大线程数量一致;
newSingleThreadExecutor:是创建只能并发执行一个线程的线程池;
newScheduledThreadPool:创建一个可缓存的的线程池,并支持定时及周期性任务执行。
newCachedThreadPool:是创建缓存线程池,若线程池长度超过任务处理需求,则回收线程,否则创建新线程执行任务;

newFixedThreadPool、newSingleThreadExecutor方法比较好理解,都是创建固定大小的线程池;

newScheduledThreadPool比Timer更安全,功能更强大,示例:

//延迟3秒执行任务
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
        scheduledExecutorService.schedule(new Runnable() {  
            @Override
            public void run() {
                // TODO Auto-generated method stub
                System.out.println("delay 3 seconds");
            }
        }, 3, TimeUnit.SECONDS);

//延迟2秒后,每隔3秒执行一次
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
        scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                // TODO Auto-generated method stub
                System.out.println("delay 2 seconds and rate 3 seconds");
            }
        }, 2, 3, TimeUnit.SECONDS);

newCachedThreadPool方法的最大线程池数量是Integer.MAX_VALUE,所有当并发任务量大时,线程池长度变大,有可能造成内存不足,系统崩溃,例如引用其他作者遇到的情况:使用无限大小线程池 newCachedThreadPool 可能遇到的问题

因此,创建线程池建议使用newFixedThreadPool,但是如何确定线程池的大小呢?需要结合服务器资源、业务需求等综合考虑。查看其他作者的文章,线程池大小的确定要考虑应用系统的类型,应用系统类型一般分为计算密集型和IO密集型。
计算密集型的应用线程池大小设置公式为:
线程数 = cpu核数+1

IO密集型应用线程池大小设置公式为:
公式1:线程数=cpu核数/(1-阻塞系数); 其中阻塞系统取值为0.8-0.9参考

公式2:线程数=cpu核数*2+1 ;是否可行待确认;

公式3:线程数=((线程等待数据+线程cpu时间)/线程cpu时间)*cpu核数;当服务器只部署这一个应用并且只要这一个线程池,这估算可能合理,待确认。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值