基于线程池实现多线程任务

首先,让我们问一个问题,为什么要使用线程池?很多时候,我们使用多线程来执行任务的时候都是这样一个场景:每新建一个任务就对应地创建一个线程,而往往每个任务在很短的时间内就结束了,导致线程的创建和销毁频繁发生,这会降低系统的效率。线程池的应用便应运而生。

一,Java线程池

1,ThreadPoolExecutor

ThreadPoolExecutor是线程池任务执行器。其继承链为

ThreadPoolExecutor extends AbstractExecutorService implements ExecutorServiceextendsExecutor

ThreadPoolExecutor的几个重要构造参数包括以下:

1)corePoolSize:核心池尺寸,当池大小小于corePoolSize时,就新建线程来执行任务。当池大小等于corePoolSize时,就将任务放进workQueue阻塞队列。池子中的空闲线程去队列中获取任务来执行。

2)maximumPoolSize:最大池尺寸,当队列已经满了,就新建线程入池来处理请求,但是线程数量不能超过最大池尺寸。

3)keepAliveTime:线程存活时间,决定当一个线程空闲了多长时间后被销毁。只有在线程池尺寸大于corePoolSize时才起作用。

4)unit:时间单位,有7种选择:

TimeUnit.DAYS;              //天

TimeUnit.HOURS;            //小时

TimeUnit.MINUTES;          //分钟

TimeUnit.SECONDS;          //秒

TimeUnit.MILLISECONDS;     //毫秒

TimeUnit.MICROSECONDS;     //微妙

TimeUnit.NANOSECONDS;     //纳秒

5)workQueue:阻塞队列,用来存储待执行的任务。声明类型为BlockingQueue,有以下几种选择:

ArrayBlockingQueue

基于数组,先进先出,必须指定大小

LinkedBlockingQueue

基于链表,先进先出,如未指定大小,则为Integer.MAX_VALUE

SynchronousQueue

没有尺寸的概念,不会保存提交的任务,而是将直接新建一个线程来执行新来的任务

PriorityBlockingQueue

其存储的对象必须实现Comparable接口,队列根据compare接口方法确定任务的优先级

 

常用的是LinkedBlockingQueue和SynchronousQueue

6)threadFactory,线程工厂,用来创建线程。

7)handler,拒绝处理任务时的策略,包括

ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。

ThreadPoolExecutor.DiscardPolicy:丢弃任务,不抛出异常。

ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)

ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

ThreadPoolExecutor的几个重要方法

1)execute()

向线程池提交任务,交由线程池来执行

2)submit()

同样是向线程池提交任务,其内部调用了execute(),区别是,会返回一个FutureTask实例

3)shutdown()

等任务都执行完毕后关闭线程池,并且拒绝接受新的任务

4)shutdownNow()

停止正在执行的任务,立刻关闭线程池,拒绝接受新的任务

5)isTerminated()

检查线程池是否已关闭

 

代码示例:

任务类

 

package com.wxy.popcorn.threadpool;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Task implements Runnable {
    private Logger logger = LoggerFactory.getLogger(getClass());
    private String tName;

    public Task(String tName) {
        this.tName = tName;
    }

    public void run() {
        logger.info("{} is executing the {}", Thread.currentThread().getName(), tName);
    }
}

任务执行器

 

 

package com.wxy.popcorn.threadpool;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class TaskExecutor {
    private Logger logger = LoggerFactory.getLogger(getClass());
    private int executeTimes;
    private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(4, 10, 2000L,
            TimeUnit.MILLISECONDS, new ArrayBlockingQueue(100), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());

    public TaskExecutor(int executeTimes) {
        this.executeTimes = executeTimes;
    }

    public void executeTasks() {
        for (int i=0; i< executeTimes; i++) {
            threadPoolExecutor.execute(new Task("task"+i));
        }
        threadPoolExecutor.shutdown();
        while (true) {
            if (threadPoolExecutor.isTerminated()) {
                logger.info("==========End to execute the tasks==========");
                break;
            }
        }
    }
}

测试类

 

 

package com.wxy.popcorn.threadpool;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class TaskExecutorTest {
    private Logger logger = LoggerFactory.getLogger(getClass());

    @Test
    public void test() {
        logger.info("==========Begin to execute tasks==========");
        new TaskExecutor(20).executeTasks();
    }
}

测试结果

 

 

2017-12-25 23:15:28.735 [main] INFO  [TaskExecutorTest.java:12] ==========Begin to execute tasks==========
2017-12-25 23:15:28.753 [pool-2-thread-3] INFO  [Task.java:15] pool-2-thread-3 is executing the task2
2017-12-25 23:15:28.753 [pool-2-thread-1] INFO  [Task.java:15] pool-2-thread-1 is executing the task0
2017-12-25 23:15:28.753 [pool-2-thread-2] INFO  [Task.java:15] pool-2-thread-2 is executing the task1
2017-12-25 23:15:28.756 [pool-2-thread-3] INFO  [Task.java:15] pool-2-thread-3 is executing the task4
2017-12-25 23:15:28.756 [pool-2-thread-1] INFO  [Task.java:15] pool-2-thread-1 is executing the task5
2017-12-25 23:15:28.756 [pool-2-thread-4] INFO  [Task.java:15] pool-2-thread-4 is executing the task3
2017-12-25 23:15:28.756 [pool-2-thread-2] INFO  [Task.java:15] pool-2-thread-2 is executing the task6
2017-12-25 23:15:28.756 [pool-2-thread-3] INFO  [Task.java:15] pool-2-thread-3 is executing the task7
2017-12-25 23:15:28.757 [pool-2-thread-1] INFO  [Task.java:15] pool-2-thread-1 is executing the task8
2017-12-25 23:15:28.757 [pool-2-thread-4] INFO  [Task.java:15] pool-2-thread-4 is executing the task9
2017-12-25 23:15:28.757 [pool-2-thread-2] INFO  [Task.java:15] pool-2-thread-2 is executing the task10
2017-12-25 23:15:28.757 [pool-2-thread-3] INFO  [Task.java:15] pool-2-thread-3 is executing the task11
2017-12-25 23:15:28.757 [pool-2-thread-1] INFO  [Task.java:15] pool-2-thread-1 is executing the task12
2017-12-25 23:15:28.757 [pool-2-thread-4] INFO  [Task.java:15] pool-2-thread-4 is executing the task13
2017-12-25 23:15:28.757 [pool-2-thread-2] INFO  [Task.java:15] pool-2-thread-2 is executing the task14
2017-12-25 23:15:28.757 [pool-2-thread-3] INFO  [Task.java:15] pool-2-thread-3 is executing the task15
2017-12-25 23:15:28.757 [pool-2-thread-1] INFO  [Task.java:15] pool-2-thread-1 is executing the task16
2017-12-25 23:15:28.757 [pool-2-thread-4] INFO  [Task.java:15] pool-2-thread-4 is executing the task17
2017-12-25 23:15:28.757 [pool-2-thread-2] INFO  [Task.java:15] pool-2-thread-2 is executing the task18
2017-12-25 23:15:28.758 [pool-2-thread-3] INFO  [Task.java:15] pool-2-thread-3 is executing the task19
2017-12-25 23:15:28.758 [main] INFO  [TaskExecutor.java:28] ==========End to execute the tasks==========

 

 

 

 

 

2,其它Java线程池

除了ThreadPoolExecutor,jdk还提供了其它多种线程池。通过Executors类的成员方法来生成实例。

1)newCachedThreadPool

 

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue());
    }

 

2)newFixedThreadPool

 

public static ExecutorService newFixedThreadPool(intnThreads){

return new ThreadPoolExecutor(nThreads,nThreads,

0L,TimeUnit.MILLISECONDS,

new LinkedBlockingQueue());

}

 

 

 

 

 

3)newScheduledThreadPool,创建一个无尺寸上限的线程池,支持定时和周期性任务。

 

public static ScheduledExecutorService newScheduledThreadPool(intcorePoolSize){

return new ScheduledThreadPoolExecutor(corePoolSize);

}

 

4)newSingleThreadExecutor,只会使用唯一的线程来执行任务,保证所有的任务都按照指定顺序执行。

 

public static ExecutorService newSingleThreadExecutor(){

return new FinalizableDelegatedExecutorService

(new ThreadPoolExecutor(1,1,

0L,TimeUnit.MILLISECONDS,

new LinkedBlockingQueue()));

}

 

二,spring线程池

ThreadPoolTaskExecutor是spring提供的线程池。其使用与Java线程池类似,不做赘述。直接查看示例代码。这里使用spring-boot的方式来进行配置。

任务类:

 

package com.wxy.popcorn.springthreadpool;

import java.util.concurrent.CountDownLatch;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SpringTask implements Runnable{
    private Logger logger = LoggerFactory.getLogger(getClass());
    private String tName;
    private CountDownLatch endGate;

    public SpringTask(String tName) {
        this.tName = tName;
    }

    public void run() {
        logger.info("{} is executing the {}", Thread.currentThread().getName(), tName);
    }
}

配置类

 

 

package com.wxy.popcorn.springthreadpool;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@Configuration
@PropertySource("classpath:threadpool.properties")
public class SpringConfig {
    @Bean
    @ConfigurationProperties(prefix = "thread.pool")
    public ThreadPoolTaskExecutor taskExecutor() {
        return new ThreadPoolTaskExecutor();
    }
}

 

属性文件

 

#线程池
#线程池维护线程的最少数量
thread.pool.core-pool-size=10
#线程池维护线程的最大数量
thread.pool.max-pool-size=20
#缓存队列
thread.pool.queue-capacity=100
#允许的空闲时间
thread.pool.keep-alive-seconds=300

启动类

 

 

package com.wxy.popcorn.springthreadpool;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringApp {
    public static void main(String[] args) {
        SpringApplication.run(SpringApp.class, args);
    }
}

测试类

 

 

package com.wxy.popcorn.springthreadpool;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringTaskExecutorTest {
    @Autowired
    ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Test
    public void test() {
        for (int i=0; i< 20; i++) {
            threadPoolTaskExecutor.execute(new SpringTask("task"+i));
        }
    }
}

测试结果

 

 

2017-12-25 23:08:59.215 [taskExecutor-2] INFO  [SpringTask.java:18] taskExecutor-2 is executing the task1
2017-12-25 23:08:59.215 [taskExecutor-5] INFO  [SpringTask.java:18] taskExecutor-5 is executing the task4
2017-12-25 23:08:59.215 [taskExecutor-9] INFO  [SpringTask.java:18] taskExecutor-9 is executing the task8
2017-12-25 23:08:59.216 [taskExecutor-4] INFO  [SpringTask.java:18] taskExecutor-4 is executing the task3
2017-12-25 23:08:59.216 [taskExecutor-3] INFO  [SpringTask.java:18] taskExecutor-3 is executing the task2
2017-12-25 23:08:59.215 [taskExecutor-1] INFO  [SpringTask.java:18] taskExecutor-1 is executing the task0
2017-12-25 23:08:59.215 [taskExecutor-7] INFO  [SpringTask.java:18] taskExecutor-7 is executing the task6
2017-12-25 23:08:59.215 [taskExecutor-6] INFO  [SpringTask.java:18] taskExecutor-6 is executing the task5
2017-12-25 23:08:59.215 [taskExecutor-8] INFO  [SpringTask.java:18] taskExecutor-8 is executing the task7
2017-12-25 23:08:59.216 [taskExecutor-10] INFO  [SpringTask.java:18] taskExecutor-10 is executing the task9
2017-12-25 23:08:59.224 [taskExecutor-2] INFO  [SpringTask.java:18] taskExecutor-2 is executing the task10
2017-12-25 23:08:59.224 [taskExecutor-5] INFO  [SpringTask.java:18] taskExecutor-5 is executing the task11
2017-12-25 23:08:59.224 [taskExecutor-9] INFO  [SpringTask.java:18] taskExecutor-9 is executing the task12
2017-12-25 23:08:59.224 [taskExecutor-4] INFO  [SpringTask.java:18] taskExecutor-4 is executing the task13
2017-12-25 23:08:59.224 [taskExecutor-3] INFO  [SpringTask.java:18] taskExecutor-3 is executing the task14
2017-12-25 23:08:59.224 [taskExecutor-1] INFO  [SpringTask.java:18] taskExecutor-1 is executing the task15
2017-12-25 23:08:59.225 [taskExecutor-7] INFO  [SpringTask.java:18] taskExecutor-7 is executing the task16
2017-12-25 23:08:59.225 [taskExecutor-6] INFO  [SpringTask.java:18] taskExecutor-6 is executing the task17
2017-12-25 23:08:59.225 [taskExecutor-8] INFO  [SpringTask.java:18] taskExecutor-8 is executing the task18
2017-12-25 23:08:59.225 [taskExecutor-10] INFO  [SpringTask.java:18] taskExecutor-10 is executing the task19

 

 

 

 

 

 

 

你可能感兴趣的:(线程池,spring,多线程,并发)