多线程 线程池 实战

一个 java web 多线程,线程池实际案例

要求:根据接口的内容,同时运行多个sql查询,并且把返回的数据整合起来。

框架spring mvc,mybatis

单线程的代码

    MyThread myThread=new MyThread();
    @PostMapping("/sql")
    @ApiOperation(value="并发sql")
    public List<JSONObject> sql(@RequestBody JSONObject stringList)
    {
        long start=System.currentTimeMillis();   //获取开始时间
        //建立线程安全的List存储多线程返回的数据
            ArrayList<JSONObject> jsonObjectList=new ArrayList();

        stringList.forEach((key,val)->{
                if (!(val + "").isEmpty()) {
                    //通过工厂执行查询(有参)
                    jsonObjectList.addAll(Factory.getInvokeStrategy(key).Sql(JSON.parseObject(val + "")));
                } else {
                    //通过工厂执行查询(无参)
                    jsonObjectList.addAll(Factory.getInvokeStrategy(key).Sql(new JSONObject()));
                }
        });
        long end=System.currentTimeMillis(); //获取结束时间
        System.out.println("程序运行时间: "+(end-start)+"ms");
        return jsonObjectList;
    }

请求体{"xxxx":"xxxxx","xxx":"xxx","xxxx":"xxx"}(参数内容不是重点不做赘述)

同时运行3条sql时的时间

程序运行时间: 268ms

多线程代码

    MyThread myThread=new MyThread();
    @PostMapping("/sql")
    @ApiOperation(value="并发sql")
    public List<JSONObject> sql(@RequestBody JSONObject stringList)
    {
        long start=System.currentTimeMillis();   //获取开始时间
        //建立线程安全的List存储多线程返回的数据
        CopyOnWriteArrayList<JSONObject> jsonObjectList=new CopyOnWriteArrayList();
        CountDownLatch countDownLatch=new CountDownLatch(stringList.size());
        stringList.forEach((key,val)->{
            myThread.addThread(new Thread(() -> {
                if (!(val + "").isEmpty()) {
                    //通过工厂执行查询(有参)
                    jsonObjectList.addAll(Factory.getInvokeStrategy(key).Sql(JSON.parseObject(val + "")));
                    countDownLatch.countDown();
                } else {
                    //通过工厂执行查询(无参)
                    jsonObjectList.addAll(Factory.getInvokeStrategy(key).Sql(new JSONObject()));
                    countDownLatch.countDown();
                }
            }));
        });
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long end=System.currentTimeMillis(); //获取结束时间
        System.out.println("程序运行时间: "+(end-start)+"ms");
        return jsonObjectList;
    }

程序运行时间: 93ms

速度提升三倍,

下面来讲一下上面的多线程代码

myThread是我自己随便封装的线程池

public class MyThread {

   private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 10, 100, TimeUnit.MILLISECONDS,
            new ArrayBlockingQueue<Runnable>(500));
    public void addThread(Thread thread) {
            threadPoolExecutor.execute(thread);
    }
    public int getActiveCount (){
        return threadPoolExecutor.getActiveCount() ;
    }

    /**
     * 结束线程池(不能向线程池中添加新的线程)
     */
    public void end(){
        threadPoolExecutor.shutdown();
    }
    public boolean isEnd(){
       return threadPoolExecutor.isTerminated();
    }

}

既然是多线程,那原本的用于存储返回值的list由于线程不安全,不可以使用。这里可以用java提供的

CopyOnWriteArrayList

继承了list同时又是线程安全的。

   CopyOnWriteArrayList<JSONObject> jsonObjectList=new CopyOnWriteArrayList();

利用线程池新建和管理子线程。(中间的业务代码,是标准的利用Spring自动注册的工厂,不清楚的可以自行去了解)

       stringList.forEach((key,val)->{
            myThread.addThread(new Thread(() -> {
                if (!(val + "").isEmpty()) {
                    //通过工厂执行查询(有参)
                    
              jsonObjectList.addAll(Factory.getInvokeStrategy(key).Sql(JSON.parseObject(val + "")));
                    countDownLatch.countDown();
                } else {
                    //通过工厂执行查询(无参)
                    jsonObjectList.addAll(Factory.getInvokeStrategy(key).Sql(new JSONObject()));
                    countDownLatch.countDown();
                }
            }));
        });

子线程在线程池的管理下自动执行,但此时的主线程,也在运行。由于主线程不需访问数据库,主线程会先于子线程把数据传出去,此时传出的数据就会是空值。

因此我们需要,让主线程停下来等待子线程执行完毕。

阻塞主线程的办法有很多,如添加while,join,或者用CountDownLatch计数,

额外注意一下,由于使用了线程池,子线程可能不会自动结束,因此利用join阻塞主线程的方法在这里无法使用。

这里我使用CountDownLatch

CountDownLatch countDownLatch=new CountDownLatch(stringList.size());
         myThread.addThread(new Thread(() -> {
                if (!(val + "").isEmpty()) {
                    //通过工厂执行查询(有参)
                    jsonObjectList.addAll(Factory.getInvokeStrategy(key).Sql(JSON.parseObject(val + "")));
                    countDownLatch.countDown();
                } else {
                    //通过工厂执行查询(无参)
                    jsonObjectList.addAll(Factory.getInvokeStrategy(key).Sql(new JSONObject()));
                    countDownLatch.countDown();
                }
            }));
    try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

到这里,代码就结束了。

个人认为这是一种很优雅的写法,尽可能的简化了代码,(使用原生的lock锁简直是让人抓狂)

以上观点皆为菜鸟瞎说,如有错误,欢迎指正

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
是的,如果你有10个不同的SQL查询,并且想要使用线程池执行它们,你可以将每个查询封装为一个任务,并将这些任务提交给线程池执行。这样可以更好地管理和控制线程数量,提高查询效率。 下面是一个示例代码,展示如何使用线程池执行10个不同的SQL查询任务: ```java import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class MainThread { public static void main(String[] args) { // 创建一个固定大小的线程池,线程数量为10 ExecutorService executorService = Executors.newFixedThreadPool(10); // 提交10个任务给线程池执行 executorService.submit(new SqlQuery("SELECT * FROM table1")); executorService.submit(new SqlQuery("SELECT * FROM table2")); // 提交更多的任务... // 关闭线程池 executorService.shutdown(); // 等待所有任务执行完毕 try { executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); } catch (InterruptedException e) { e.printStackTrace(); } // 所有查询执行完毕后的后续处理 } } ``` 在上面的代码中,我们创建了一个固定大小的线程池,线程数量为10。然后,我们使用`executorService.submit()`方法提交10个不同的SQL查询任务给线程池执行。最后,我们关闭线程池,并使用`executorService.awaitTermination()`方法等待所有任务执行完毕。 通过使用线程池,你可以更好地管理和控制线程数量,并且能够在任务执行完毕后进行后续处理。线程池会自动重用线程,避免频繁地创建和销毁线程,提高了性能和资源利用率。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值