线程池(二)ThreadPoolExecutor(2)集成demo

一、ThreadFactory自定义线程创建

1、自定义ThreadFactory

 线程池中线程就是通过ThreadPoolExecutor中的ThreadFactory,线程工厂创建的。自定义ThreadFactory,可以按需要对线程池中创建的线程进行一些特殊的设置,如命名、优先级等,如:

package exceldemo;

import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.*;

public class Demo {

   private static ThreadPoolExecutor pool;

    public static void main( String[] args )
    {
        //自定义线程工厂
                pool = new ThreadPoolExecutor(1, 2, 1000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(5),
                new ThreadFactory() {
                    public Thread newThread(Runnable r) {
                        System.out.println("线程"+r.hashCode()+"创建");
                        //线程命名
                        Thread th = new Thread(r,"threadPool"+r.hashCode());
                        return th;
                    }
                }, new ThreadPoolExecutor.CallerRunsPolicy());

        for(int i=0;i<10;i++) {
            pool.execute(new DemoTask());
        }
    }
}


结果:
线程2061475679创建
线程140435067创建
main
main
main
threadPool2061475679
threadPool2061475679
threadPool2061475679
threadPool2061475679
threadPool2061475679
threadPool2061475679
threadPool140435067
2、ThreadPoolExecutor扩展 

ThreadPoolExecutor扩展主要是围绕beforeExecute()、afterExecute()和terminated()三个接口实现的,

beforeExecute:线程池中任务运行前执行

afterExecute:线程池中任务运行完毕后执行

terminated:线程池退出后执行

demo:

package exceldemo;

import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.*;

public class Demo {

 private static ThreadPoolExecutor pool;

    public static void main( String[] args )
    {
        //自定义线程工厂
                //实现自定义接口
                pool = new ThreadPoolExecutor(2, 4, 1000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(5),
                new ThreadFactory() {
                    public Thread newThread(Runnable r) {
                        System.out.println("线程"+r.hashCode()+"创建");
                        //线程命名
                        Thread th = new Thread(r,"threadPool"+r.hashCode());
                        return th;
                    }
                },
                new ThreadPoolExecutor.CallerRunsPolicy()) {

            @Override
            protected void beforeExecute(Thread t,Runnable r) {
                System.out.println("准备执行:"+ ((DemoTask)r).getTaskName());
            }

            @Override
            protected void afterExecute(Runnable r,Throwable t) {
                System.out.println("执行完毕:"+((DemoTask)r).getTaskName());
            }

            @Override
            protected void terminated() {
                System.out.println("线程池退出");
            }
        };

        for(int i=0;i<10;i++) {
            pool.execute(new DemoTask("Task"+i));
        }
        pool.shutdown();

    }
}
package exceldemo;

public class DemoTask implements Runnable{

    private String taskName;
    public String getTaskName() {
        return taskName;
    }
    public void setTaskName(String taskName) {
        this.taskName = taskName;
    }
    public DemoTask(String name) {
        this.setTaskName(name);
    }

    @Override
    public void run() {
        //输出执行线程的名称
        System.out.println("TaskName"+this.getTaskName()+"---ThreadName:"+Thread.currentThread().getName());
    }
}

 输出:

执行结果:
线程2061475679创建
线程140435067创建
线程1450495309创建
准备执行:Task0
线程1670782018创建
TaskNameTask0---ThreadName:threadPool2061475679
TaskNameTask9---ThreadName:main
执行完毕:Task0
准备执行:Task2
准备执行:Task1
TaskNameTask2---ThreadName:threadPool2061475679
执行完毕:Task2
准备执行:Task3
TaskNameTask3---ThreadName:threadPool2061475679
准备执行:Task7
执行完毕:Task3
TaskNameTask7---ThreadName:threadPool1450495309
执行完毕:Task7
准备执行:Task5
准备执行:Task4
TaskNameTask5---ThreadName:threadPool1450495309
TaskNameTask4---ThreadName:threadPool2061475679
TaskNameTask1---ThreadName:threadPool140435067
执行完毕:Task5
执行完毕:Task1
准备执行:Task6
TaskNameTask6---ThreadName:threadPool1450495309
执行完毕:Task4
执行完毕:Task6
准备执行:Task8
TaskNameTask8---ThreadName:threadPool1670782018
执行完毕:Task8
线程池退出

二、集成规范

 1、配置ThreadPoolExecutor

整个项目中一般只配置一个ThreadPoolExecutor;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
 
/**
 * 线程池配置
 * 根据业务不同(有的业务执行快,有的业务执行慢),使用不同的线程池
 * 自定义线程工厂
 * 自定义拒绝策略,暂时用"调用者运行策略"
 */
@Configuration
public class ThreadPoolConfig {
 
    /**
     * 核心线程数
     */
    private static final int corePoolSize = 8;
 
    /**
     * 线程池中允许的最大线程数
     */
    private static final int maximumPoolSize = 16;
 
    /**
     * 线程空闲超时时间(秒)
     */
    private static final long keepAliveTime = 30;
 
    /**
     * 任务队列
     */
    private static final int capacity = 100;
 
    /**
     * 站内信、短信-线程池
     *
     * @return
     */
    @Bean(name = "threadPool")
    public ThreadPoolExecutor msgThreadPoolExecutor() {
        return new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(capacity),
                new CustomThreadFactory("msg"),
                new ThreadPoolExecutor.CallerRunsPolicy());
    }
}

如果线程应用的比较多,也可以配置多个线程池:比如

线程池1:@Bean(name="serviceJobTaskExecutor")
线程池2 :@Bean(name="msgJobTaskExecutor")
2、调用

调用处直接获取bean,再根据具体业务提交线程即可。

 @Autowired 
 private static ThreadPoolExecutor msgJobTaskExecutor;

注意:切记子线程与主线程(调用处)是异步执行的,需要注意参数传递的正确性,

举例如下:如用户表有id、user_name、age,现在根据id集合查询用户列表,当id集合过大,使用in查询效率低,如果采用循环,单线程又比较耗时,这时可以使用多线程去处理。

package exceldemo.service.impl;

import exceldemo.dto.User;
import exceldemo.service.UserService;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service("userService")
public class UserServiceImpl implements UserService {
    @Override
    public List<User> getByIds(List<Integer> ids) {

        List<User> users = new ArrayList<>();
        for(Integer id : ids){
            User user = new User();
            user.setAge(id);
            user.setUserName("用户"+id);
            //耗时操作
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            users.add(user);
        }
        return users;
    }
}

先看普通的单线程写法: 

package exceldemo.rest;

import exceldemo.dto.User;
import exceldemo.service.UserService;
import exceldemo.thread.MyThreadPoolExecutor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;



    //获取所有用户
    @RequestMapping("/getAll")
    public List<User> getAll( ){
        List<Integer> ids = new ArrayList<>();
        for(int i = 0;i<=500;i++){
            ids.add(i);
        }
        long startTime = new Date().getTime();
        List<User> users = new ArrayList<>();
        List<Integer> queryIds = new ArrayList<>();
        for(Integer id : ids){
            queryIds.add(id);
            if(queryIds.size() == 100){
                users.addAll(userService.getByIds(queryIds));
                queryIds.clear();
            }
        }
        if(queryIds.size() > 0){
            users.addAll(userService.getByIds(queryIds));
        }
        long endTime = new Date().getTime();
        System.out.println("耗时"+(endTime-startTime));
        return users;
    }
}

请求后后端打印:

耗时5055

多线程:

package exceldemo.thread;

import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

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

public class MyThreadPoolExecutor {

    private static ThreadPoolExecutor threadPoolExecutor = new  ThreadPoolExecutor(10,
            21,
            1000,
            TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>(),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());;

    public static ThreadPoolExecutor getThreadPoolInstance(){
          System.out.println(threadPoolExecutor);
          return threadPoolExecutor;
    }
}
package exceldemo.task;

import exceldemo.dto.User;
import exceldemo.service.UserService;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;

public class UserTask implements Callable<List<User>> {

    private List<Integer> userIds;

    private UserService userService;

    public UserTask(List<Integer> userIds,UserService userService) {
        this.userIds = userIds;
        this.userService = userService;
    }

    @Override
    public List<User> call() throws Exception {

        List<User> users = userService.getByIds(userIds);
        return users;
    }
}
package exceldemo.rest;

import exceldemo.dto.User;
import exceldemo.service.UserService;
import exceldemo.task.UserTask;
import exceldemo.thread.MyThreadPoolExecutor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;



    //获取所有用户
    @RequestMapping("/getAll")
    public List<User> getAll( ){
        List<Integer> ids = new ArrayList<>();
        for(int i = 0;i<=500;i++){
            ids.add(i);
        }
        long startTime = new Date().getTime();
        //List<User> users = userService.getByIds(ids);
        //多线程处理开始*******************************************
        List<Future> futures = new ArrayList<>();
        for (int i = 0; i < ids.size(); i += 100) {
            int startIndex = i;
            int endIndex = startIndex + 100 > ids.size() ? ids.size() : startIndex + 100;
            UserTask task = new UserTask(ids.subList(startIndex, endIndex),userService);
            Future<List<User>> future = MyThreadPoolExecutor.getThreadPoolInstance().submit(task);
            futures.add(future);
        }
        //取数据
        List<User> users = new ArrayList<>();
        try{
            for (Future futureIter : futures) {
                List<User> personnelDetailDOS = (List<User>) futureIter.get();
                users.addAll(personnelDetailDOS);
            }
        }catch (Exception e){

        }

        //多线程处理结束*******************************************

        long endTime = new Date().getTime();
        System.out.println("耗时"+(endTime-startTime));
        return users;
    }
}

结果:

耗时1023

获取的结果也和单线程是一样的,完全正确。

下面采用一种错误的批量写法来看下:

ThreadPoolExecutor excutor = MyThreadPoolExecutor.getThreadPoolInstance();  
for(Integer id : ids){
            queryIds.add(id);
            if(queryIds.size() == 100){
                UserTask task = new UserTask(queryIds,userService);
                Future<List<User>> future = excutor.submit(task);
                futures.add(future);
                queryIds.clear();
            }
        }
        if(queryIds.size() > 0){
            UserTask task = new UserTask(queryIds,userService);
            Future<List<User>> future = excutor.submit(task);
            futures.add(future);
        }

请求的结果不正确且随机:

[
  {
    "userName": "用户100",
    "age": 100
  },
  {
    "userName": "用户300",
    "age": 300
  },
  {
    "userName": "用户300",
    "age": 300
  },
  {
    "userName": "用户400",
    "age": 400
  },
  {
    "userName": "用户500",
    "age": 500
  },
  {
    "userName": "用户500",
    "age": 500
  }
]

但是在单线程中controller批量调用dubbo服务这样写是没有问题的,这是因为应用多线程时,submit(execute)方法实际调用的为Callable(Runnable)的call(run),这个call和run与主线程不是同步在执行。如在本例中传递的是一个对象,而对象是引用传递,那可能有很多种情况:

1)如果call在queryIds.clear之后,在且只在下一个childIds.add(100)之后被调度执行,这时call方法里面拿到的对象List<Integer>就是一个100了;

2)如果call在queryIds.clear之后,且在下一个childIds.add(id)之前被调度执行,这时call方法里面拿到的对象List<Integer>就是空了

......

而第一种写法给每个UserTask传递的参数就是一个List,且这个List在它所在的UserTask生命周期中没有被改变,所以结果是正确的。由此可见,在异步操作时要注意参数的一致性。

当然,第二种种批量写法这样改造也是正确的:

ThreadPoolExecutor excutor = MyThreadPoolExecutor.getThreadPoolInstance();  
for(Integer id : ids){
            queryIds.add(id);
            if(queryIds.size() == 100){
               List<Integer> copyIds = new ArrayList<>();
                for(Integer copyId : queryIds){
                    copyIds.add(copyId);
                }
                UserTask task = new UserTask(copyIds,userService);
                Future<List<User>> future = excutor.submit(task);
                futures.add(future);
                queryIds.clear();
            }
        }
        if(queryIds.size() > 0){
            UserTask task = new UserTask(queryIds,userService);
            Future<List<User>> future = excutor.submit(task);
            futures.add(future);
        }

 execute提交保证线程安全:

List<User> users = userMapper.selectAll();
List<List<User>> userPageList= Lists.partition(users,100);       
for(int page = 0;page < userPageList.size();page++){
                int finalPage = page;
                threadPoolTaskExecutor.execute(()->{
                    List<User> statPersons = userPageList.get(finalPage);
                   //逻辑
                });
            }

  • 15
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

w_t_y_y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值