java线程池使用

1.最简单入门

public class ThreadTest {

	@Test
	public void testExecute() 
	{
		ExecutorService executorService = Executors.newFixedThreadPool(3);
		for (int i = 0; i < 5; i++) 
		{
			executorService.execute(new MyThread());
		}
		executorService.shutdown();
	}

}

class MyThread implements Runnable
{
	private static final Logger logger = LoggerFactory.getLogger(MyThread.class);
	
	public void run() 
	{
		logger.info("I am running...");
	}
}
           执行结果为:

2015/06/17 20:02:56,594 - INFO - pool-1-thread-2 - I am running...
2015/06/17 20:02:56,595 - INFO - pool-1-thread-1 - I am running...
2015/06/17 20:02:56,595 - INFO - pool-1-thread-1 - I am running...
2015/06/17 20:02:56,597 - INFO - pool-1-thread-3 - I am running...
2015/06/17 20:02:56,597 - INFO - pool-1-thread-3 - I am running...
        说明线程池已经起了作用。
        注意:其中的shutdown并不是关闭线程池,而是不再接受新的执行任务,当其中线程执行完成之后,再关闭

2.相关概念

    (1)ExecutorService

               看一下ExecutorService的源码:

public interface ExecutorService extends Executor {  
  
    void shutdown();  
  
    List<Runnable> shutdownNow();  
  
    boolean isShutdown();  
  
    boolean isTerminated();  
  
    boolean awaitTermination(long timeout, TimeUnit unit)  
  
    <T> Future<T> submit(Callable<T> task);  
  
    <T> Future<T> submit(Runnable task, T result);  
  
    Future<?> submit(Runnable task);  
  
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)  
  
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)  
  
    <T> T invokeAny(Collection<? extends Callable<T>> tasks)  
  
    <T> T invokeAny(Collection<? extends Callable<T>> tasks,  
                    long timeout, TimeUnit unit)  
} 
         而ExecutorService又继承了Executor接口

public interface Executor {
    void execute(Runnable command);
}
       所以,ExecutorService就是线程池的实际执行操作接口,那么有了接口具体的实现是什么呢?

      jdk给我们定义了多个线程池的具体实现,可以通过Executors来获得

    (2)Executors

               用来获得线程池的工具类

              1)创建固定线程数的线程池

public static ExecutorService newFixedThreadPool(int nThreads) 
                  创建一个可重用线程的、固定线程数的线程池

                   表示核心线程和最大线程数都是nThreads,也就是说如果创建的任务数超过了nThreads,那么就会将任务抛弃
              2)创建不限容量但是会过期的线程池

    public static ExecutorService newCachedThreadPool()
                  创建一个不限大小的线程池,如果其中的线程超过60秒闲置,就会被线程池回收

              3)创建单一线程的线程池

    public static ExecutorService newSingleThreadExecutor() 
                 其中只有一个线程

    (3)线程执行

               1)使用execute方法

    void execute(Runnable command);
                    最核心的方法,用来执行一个线程

               2)使用submit方法

    <T> Future<T> submit(Callable<T> task);

    <T> Future<T> submit(Runnable task, T result);

    Future<?> submit(Runnable task);
                   如果使用Callable来实现线程,那么可以用submit来获得返回的参数,但是注意:submit会吃掉异常!!!必须在通过Future.get()来获取出现的异常
                  正常情况:

public class ThreadTest {
	private static final Logger logger = LoggerFactory.getLogger(ThreadTest.class);

	@Test
	public void testExecute()
	{
		ExecutorService executorService = Executors.newFixedThreadPool(3);
		for (int i = 0; i < 5; i++) 
		{
			Future<String> result = executorService.submit(new MyThread());
			try 
			{
				String resString = result.get();
				logger.info(resString);
			}
			catch (InterruptedException e) 
			{
				e.printStackTrace();
			} 
			catch (ExecutionException e) 
			{
				e.printStackTrace();
			}
		}
		executorService.shutdown();
	}

}

class MyThread implements Callable<String>
{
	private static final Logger logger = LoggerFactory.getLogger(MyThread.class);
	
	public String call() throws Exception {
		logger.info("I am running...");
		return "success";
	}
}


2015/06/17 21:36:06,789 - INFO - pool-1-thread-1 - I am running...
2015/06/17 21:36:06,789 - INFO - pool-1-thread-1 - I am running...
2015/06/17 21:36:06,791 - INFO - main - success
2015/06/17 21:36:06,791 - INFO - main - success
2015/06/17 21:36:06,797 - INFO - pool-1-thread-2 - I am running...
2015/06/17 21:36:06,797 - INFO - pool-1-thread-2 - I am running...
2015/06/17 21:36:06,797 - INFO - main - success
2015/06/17 21:36:06,797 - INFO - main - success
2015/06/17 21:36:06,798 - INFO - pool-1-thread-3 - I am running...
2015/06/17 21:36:06,798 - INFO - main - success
             异常情况:

public class ThreadTest {
	private static final Logger logger = LoggerFactory.getLogger(ThreadTest.class);

	@Test
	public void testExecute()
	{
		ExecutorService executorService = Executors.newFixedThreadPool(3);
		for (int i = 0; i < 5; i++) 
		{
			executorService.submit(new MyThread());
		}
		executorService.shutdown();
	}

}

class MyThread implements Callable<String>
{
	private static final Logger logger = LoggerFactory.getLogger(MyThread.class);
	
	public String call() throws Exception {
		int a = 3/0;
		logger.info("I am running...");
		return "success";
	}
}
            按理说应该抛出除零异常,但是实际是程序没有任何输出,单元测试时可以通过的。

           所以必须自己手动捕获异常:

public class ThreadTest {
	private static final Logger logger = LoggerFactory.getLogger(ThreadTest.class);

	@Test
	public void testExecute()
	{
		ExecutorService executorService = Executors.newFixedThreadPool(3);
		for (int i = 0; i < 5; i++) 
		{
			Future<String> result = executorService.submit(new MyThread());
			try 
			{
				String resString = result.get();
				logger.info(resString);
			}
			catch (InterruptedException e) 
			{
				e.printStackTrace();
			} 
			catch (ExecutionException e) 
			{
				//如果一个线程出错,则所有线程都关闭
				executorService.shutdownNow();
				e.printStackTrace();
			}
		}
		executorService.shutdown();
	}

}

class MyThread implements Callable<String>
{
	private static final Logger logger = LoggerFactory.getLogger(MyThread.class);
	
	public String call() throws Exception {
		int a = 3/0;
		logger.info("I am running...");
		return "success";
	}
}
           

    (3)线程池关闭

               1)shutdown

                      不是关闭线程池,只是不能接受新的任务了,它会等待所有任务执行完毕

               2)shutdownNow

                      真正的关闭,会关闭其中的所有线程


3.自定义线程池

    (1)使用的方法

             我们创建线程池一般都是通过ThreadPoolExecutor来实现的

public class ThreadPoolExecutor extends AbstractExecutorService {
    .....
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
            BlockingQueue<Runnable> workQueue);
 
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
            BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory);
 
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
            BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler);
 
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
        BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler);
    ...
}
           这里简单看一下继承关系:
                        

 

    (2)构造参数意义

              1)corePoolSize

                    核心池大小。默认情况下,在创建了线程池之后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize之后,就会把到达的任务放到缓存队列中。

              2)maximumPoolSize

                    线程池最大线程数,表示线程池中最多能创建多少个线程

              3)keepAliveTime

                    表示线程没有任务执行时最多保持多久时间终止。

                    默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用。也就是说当线程池中的线程数量大于corePoolSize时,如果一个线程空闲时间达到keepAliveTime,则会终止,直到线程池中的线程数目不超过corePoolSize

              4)uint

                    和keepAliveTime配合使用,表示时间单位,有七种取值。

TimeUnit.DAYS;               //天
TimeUnit.HOURS;             //小时
TimeUnit.MINUTES;           //分钟
TimeUnit.SECONDS;           //秒
TimeUnit.MILLISECONDS;      //毫秒
TimeUnit.MICROSECONDS;      //微妙
TimeUnit.NANOSECONDS;       //纳秒

              5)workQueue

                    一个阻塞队列,用来存储等待执行的任务。一般有以下几种选择

ArrayBlockingQueue;
LinkedBlockingQueue;
SynchronousQueue;
                     一般使用LinkedBlockingQueue

              6)threadFactory

                    线程工厂,用来创建线程

              7)handler

                    表示当拒绝处理任务时的策略,有以下几种取值

ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务 

    (3)主要流程
               1)corePoolSize与maximumPoolSize说明
                    corePoolSize翻译成核心池大小,实际上就是线程池的大小。举个例子:

                    加入有一个工厂,里面有10个工人,每个工人只能同时做一件任务。

                    开始时10个工人都是空闲的,来了任务就分配给空闲的工人做;

                    当10个工人都有任务在做时,如果还来了任务,就把任务进行排队等待;

                    如果说新任务的增长速度远远大于工人做任务的速度,那么这时工厂主管可能会想补救措施,比如新招聘4个临时工来做,

                    然后就将新任务分配给临时工来做;

                    如果说14个工人做任务的速度还是不够,那么主管就要考虑不再接收新任务或者抛弃前面的一些任务了。

                    例子中,corePoolSize就是10,而maximumPoolSize就是(10+4)=14


               2)具体总结

  • 如果当前线程池中的线程数目小于corePoolSize,则每来一个任务,就会创建一个线程去执行这个任务;
  • 如果当前线程池中的线程数目>=corePoolSize,则每来一个任务,会尝试将其添加到任务缓存队列当中,若添加成功,则该任务会等待空闲线程将其取出去执行;若添加失败(一般来说是任务缓存队列已满),则会尝试创建新的线程去执行这个任务;
  • 如果当前线程池中的线程数目达到maximumPoolSize,则会采取任务拒绝策略进行处理;
  • 如果线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止,直至线程池中的线程数目不大于corePoolSize;如果允许为核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepAliveTime,线程也会被终止。
<think>好的,我需要回答用户关于如何在Java使用线程池的问题,包括示例代码和最佳实践。首先,我应该回顾用户提供的引用内容,看看有没有相关的信息可以利用。 根据引用[1],提到了Java线程池使用实例、最佳实践以及代码示例。引用[2]讨论了为什么使用线程池、如何使用以及遇到的坑。引用[3]则集中在最佳实践和异常处理方面。这些引用应该能提供足够的信息来构建回答。 首先,我需要解释线程池的基本概念和为什么要使用线程池,比如资源复用、控制并发数、管理线程生命周期等。然后,介绍Java中创建线程池的方法,特别是通过Executors工厂类提供的几种常见线程池类型,比如FixedThreadPool、CachedThreadPool、SingleThreadExecutor等,但也要提到推荐直接使用ThreadPoolExecutor来创建,以便更灵活地控制参数,避免资源耗尽的风险,这点在引用[3]中有提到。 接下来,给出一个示例代码。根据引用[1]中的示例,可能需要展示如何通过ThreadPoolExecutor来创建线程池,设置核心线程数、最大线程数、队列容量等参数,并演示如何提交任务。同时,需要强调在阿里巴巴开发规范中推荐手动创建线程池,而不是使用Executors,这点可能来自引用[3]中的最佳实践。 然后,讨论最佳实践,比如合理配置线程数、使用有界队列、处理未捕获异常、正确关闭线程池等。这些内容在引用[3]中有涉及,比如异常处理和分析的重要性,以及关闭线程池的方法。 此外,要注意线程池的任务拒绝策略,如AbortPolicy、CallerRunsPolicy等,并说明在不同场景下如何选择合适的策略。这部分可能需要结合引用[2]中提到的使用过程中遇到的坑。 最后,生成相关问题,可能包括线程池的参数配置、不同线程池类型的区别、如何监控线程池状态、常见的线程池问题排查等,确保问题与用户查询内容相关,并帮助深化理解。 需要确保回答结构清晰,分步骤说明,代码正确,并且引用正确的位置。例如,在提到线程池参数配置时,引用[3]中的最佳实践;在提到任务拒绝策略时,引用[2]中的实际应用经验。 现在需要将以上思考整理成中文回答,确保符合用户要求的格式,使用正确的LaTeX语法,并在适当的位置添加引用标识。</think>### 如何在Java使用线程池? #### 1. **线程池的作用与优势** - **资源复用**:避免频繁创建/销毁线程,提升性能[^2]。 - **控制并发**:通过核心线程数、最大线程数限制资源占用[^3]。 - **任务管理**:使用队列缓冲任务,支持拒绝策略处理过载[^1]。 #### 2. **创建线程池** Java推荐通过`ThreadPoolExecutor`手动配置参数(而非`Executors`工厂类,避免潜在风险)[^3]: ```java int corePoolSize = 5; // 核心线程数 int maxPoolSize = 10; // 最大线程数 long keepAliveTime = 60L; // 空闲线程存活时间(秒) BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(100); // 有界队列 ThreadPoolExecutor executor = new ThreadPoolExecutor( corePoolSize, maxPoolSize, keepAliveTime, TimeUnit.SECONDS, queue, new ThreadPoolExecutor.AbortPolicy() // 默认拒绝策略:直接抛出异常 ); ``` #### 3. **提交任务** - 使用`execute()`提交`Runnable`任务: ```java executor.execute(() -> { System.out.println("Task executed by " + Thread.currentThread().getName()); }); ``` - 使用`submit()`提交`Callable`任务(可获取返回值): ```java Future<String> future = executor.submit(() -> { return "Result from thread " + Thread.currentThread().getName(); }); System.out.println(future.get()); // 阻塞获取结果 ``` #### 4. **最佳实践** - **参数配置**: - CPU密集型任务:核心线程数 ≈ CPU核心数[^3]。 - I/O密集型任务:核心线程数可适当增加(如2倍CPU核心数)。 - 使用有界队列避免内存溢出[^3]。 - **异常处理**: ```java executor.setRejectedExecutionHandler((r, executor) -> { // 自定义拒绝策略,如记录日志或降级处理 }); // 任务内捕获异常 executor.execute(() -> { try { // 业务逻辑 } catch (Exception e) { // 记录异常 } }); ``` - **关闭线程池**: ```java executor.shutdown(); // 等待已提交任务完成 // executor.shutdownNow(); // 立即终止所有任务(慎用) ``` #### 5. **常用线程池类型** - **FixedThreadPool**:固定线程数,适用于负载较重的场景。 ```java ExecutorService fixedPool = Executors.newFixedThreadPool(5); ``` - **CachedThreadPool**:弹性线程数,适合短时异步任务[^1]。 - **ScheduledThreadPool**:支持定时/周期性任务。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值