摘要:多线程任务处理对提高性能很有帮助,在Java中提供的线程池也方便了对多线程任务的实现。使用它很简单,而如果进行了不正确的使用,那么代码将陷入一团乱麻。因此如何正确地使用它,如以下分享,这个技能你get到没?

关键词:多线程, 线程池, 数据库, 算法

解决问题:如何正确使用线程池。


 

众所周知,线程池在Java中非常常用,使用它也是一项最基本的技能。不过怎样才能更合理、更方便地使用线程池,我们需要总结一下。

下面是线程池最基础的使用方式。
ExecutorService jobPool = Executors.newFixedThreadPool();
while(true){
Job_anqi job_anqi = new GetData_anqi();
job_anqi.setParm(parm);
jobPool.submit(job_anqi);
}
 
可以从以上代码看出,产生了很多个job,我们不想按顺序完成它们,它们之间也没有任何的关系,因此“分布式任务”、mapReduce?现在还太早,步子跨太大容易扯着蛋,先把单机的多任务给完成吧。所以,我们创建了一个线程池,在循环中,不断地把job填充进去,这个线程池不大,只能容纳10个线程同时跑,其它的线程放进去后就得老老实实地排队等待。
当然这里只是一个简单的Demo,虽然它包含了“向各线程传入参数”这样东西,更复杂的还有“在主线程中获取各线程结果的返回值”。
 
  1. ExecutorService executorService =Executors.newCachedThreadPool();
    List<Future<String>> resultList =newArrayList<Future<String>>();
    // 创建10个任务并执行
    for(int i =0; i <10; i++){
    // 使用ExecutorService执行Callable类型的任务,并将结果保存在future变量中
    Future<String> future = executorService.submit(newTaskWithResult(i));
    // 将任务执行结果存储到List中
    resultList.add(future);
    }
    executorService.shutdown();
    // 遍历任务的结果
    for(Future<String> fs : resultList){
    try{
    System.out.println(fs.get());// 打印各个线程(任务)执行的结果,其中会偶尔抛出异常
    }catch(InterruptedException e){
    e.printStackTrace();
    }catch(ExecutionException e){
    executorService.shutdownNow();//某一线程发生异常时,关闭线程池
    e.printStackTrace();
    return;
    }
    }
上面这段代码不仅用到了多线程取结果值的方法,还有另外的功能:在执行某一项job时,若其发生了异常,则会连带关闭其他运行在此线程池上的所有线程。这有时是很有用的。
 
到目前为止,以上讨论的还停留在线程池的使用方法上。然而,现在的问题不是如何使用线程池,那太low了不是。当前的问题是,如何控制job的深浅,如何决定job的深浅。当你有很多任务要放在线程池中时,他们可能有各种不同的组织形式,相对的也就有不同的实现办法。例如:1、线程们可以按天来划分,每天起一个线程,多少天就起多少个线程丢在里面;2、同时,线程们也可以按数据形式来划分,每一种数据形式起一个线程,有多少种数据形式就开多少个线程;3、再者,线程干脆按粒度来划分,我每一千个数据批次包装成一个线程,无论天、无论数据形式,只是每千条数据一个线程地跑;4、甚至,你可以线程池中套线程池,每天一个线程,再在每天的数据里对每种数据形式开一个线程,再在每种数据形式里每千条数据一个线程地跑。这就有很复杂的场景了。
 
那么该如何选择使用哪一种划分线程的方式呢?我们来慢慢分析。
方案3是最常会想起的划分线程池办法,此办法划分的线程粒度最细,线程池放置的位置最深,也最方便。当然从理论上来说方案3跟其他方案一样执行起来不会有问题,所有的任务执行完毕后能够与其他方案得到同样的结果。可是,一旦我们有特殊需求时,它是不实用的。如果,你需要打印日志或者记录程序执行状态,比如说我要按天记录该天任务执行的如何,那么选择使用方案1是非常合适的,如果性能还不行,还可以选择使用方案4。因为在方案1和方案4中都可以知道该天的任务何时执行完毕。若是使用方案2,则不清楚每天的任务执行情况,因此无法实现记录;使用方案3,因为它的线程池放置位置太深,更是无法实现按天记录。
 
要实现记录任务的执行状态,那就需要对线程池进行把控,不可能将所有的任务提交到线程池中就不管了。下面是最常见的监视线程池中所有的线程是否执行完毕的代码段。
  1. ExecutorService jobPool =Executors.newFixedThreadPool(10);
    while(true){
    Job_anqi job_anqi =newGetData_anqi();
    job_anqi.setParm(parm);
    jobPool.submit(job_anqi);
    }
    pool.shutdown();
    try{
    while(!jobPool.isTerminated()){
    Thread.sleep(1000);
    }
    }catch(InterruptedException e){
    e.printStackTrace();
    }
    logger.info("angel wang 做完了所有的工作, Good job!");
可以看到,把所有的job都提交完后,就要起个while循环来监听这个线程池,每隔一秒去问一下所有的线程是不是都跑完了。在得到肯定的答复后,就可以说,"angel wang 做完了所有的工作, Good job!"了。
 
综上所述,创建线程池的位置很重要,它直接决定了划分线程的粒度,而线程的粒度是需要你来把控的。
 
PS:java.sql.Connection是非线程安全的,各线程间不能对同一个Connection实例进行操作,不然对数据库的操作会乱掉。但也要注意,如果需要启动多线程对同一个数据库进行操作,每个线程要使用其自身创建的Connection实例来连接并操作数据库,如果不加限制的话,那么对该数据库建立的连接数会过多,导致建立连接不成功。因此,要么在数据库连接池中配置最多可以创建的连接数,要么控制创建出的线程个数,进行连接池配置要方便及容易许多。这也是数据库连接池如:Druid、DBCP、C3P0存在的意义之一。
 
最近换了城市,换了份工作,进入互联网公司,遇到了很多之前没有遇到过的问题,技术上的、管理上的、沟通上的......。体会下来,真是家家有本难念的经啊,无限烧脑中。但是兵来将挡水来土掩嘛,有什么问题解决什么问题。

来自王安琪

JAVA基础拾遗-论线程池的线程粒度划分与深浅放置的更多相关文章

  1. Java基础学习笔记: 多线程,线程池,同步锁(Lock,synchronized )(Thread类,ExecutorService ,Future类)(卖火车票案例)

    多线程介绍 学习多线程之前,我们先要了解几个关于多线程有关的概念.进程:进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能. 线 ...

  2. java 线程池(线程的复用)

    一. 线程池简介 1. 线程池的概念: 线程池就是首先创建一些线程,它们的集合称为线程池.使用线程池可以很好地提高性能,线程池在系统启动时即创建大量空闲的线程,程序将一个任务传给线程池,线程池就会启动 ...

  3. 由浅入深理解Java线程池及线程池的如何使用

    前言 多线程的异步执行方式,虽然能够最大限度发挥多核计算机的计算能力,但是如果不加控制,反而会对系统造成负担.线程本身也要占用内存空间,大量的线程会占用内存资源并且可能会导致Out of Memory ...

  4. -1-5 java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁 sleep()和wait()方法的区别 为什么wait(),notify(),notifyAll()等方法都定义在Object类中

     本文关键词: java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁  sleep()和wait()方法的区别 为什么wait( ...

  5. Java多线程、线程池和线程安全整理

    多线程 1.1      多线程介绍 进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能. 1.2      Thread类 通 ...

  6. Java多线程系列 JUC线程池03 线程池原理解析(二)

    转载  http://www.cnblogs.com/skywang12345/p/3509954.html  http://www.cnblogs.com/skywang12345/p/351294 ...

  7. Java多线程系列 JUC线程池02 线程池原理解析(一)

    转载  http://www.cnblogs.com/skywang12345/p/3509960.html ; http://www.cnblogs.com/skywang12345/p/35099 ...

  8. Java多线程系列 JUC线程池01 线程池框架

    转载  http://www.cnblogs.com/skywang12345/p/3509903.html 为什么引入Executor线程池框架 new Thread()的缺点 1. 每次new T ...

  9. Java多线程系列 JUC线程池07 线程池原理解析(六)

     关闭“线程池” shutdown()的源码如下: public void shutdown() { final ReentrantLock mainLock = this.mainLock; // ...

随机推荐

  1. Sql Server来龙去脉系列之一 目录篇

    从工作一直到现在都没怎么花功夫深入学习下Sql Server数据库,在使用Sql Server时90%的时间基本上都是在接触T-SQL,所以数据库这块基本上属于菜鸟级别.至于数据库的底层框架以及运行机 ...

  2. 面向对象的JavaScript(2):类

    在小项目中对于JavaScript使用,只要写几个function就行了.但在大型项目中,尤其是在开发追求良好的用户体验的网站中,如SNS,就会 用到大量的JavaScrpt,有时JavaScript ...

  3. Jquery:ajax跨域请求处理

    昨天朋友想做个图片懒加载的效果,朋友是前端的,我这边给他提供数据,程序写好了放到服务器上,本地测试访问时却报jquery跨域的问题,于是找度娘了解了一下jquey如何处理,网上有很多参考文章,但没细看 ...

  4. [迷宫中的算法实践]迷宫生成算法——递归分割算法

    Recursive division method        Mazes can be created with recursive division, an algorithm which wo ...

  5. C#基础-css行内元素、块级元素基础

    一.行内元素与块级元素 块级元素列表 <address> 定义地址 <caption> 定义表格标题 <dd> 定义列表中定义条目 <div> 定义文档 ...

  6. [WCF实践]1.WCF使用net.tcp寄宿到IIS中

    一.IIS部分 环境:Windows Server 2008 R2 1.安装WAS,如下图所示:   2.网站net.tcp协议绑定,如下图所示:   3.网站启用net.tcp,如下图所示:   二 ...

  7. 运用Microsoft.DirectX.DirectSound和Microsoft.DirectX实现简单的录音功能

    1.首先要使用Microsoft.DirectX.DirectSound和Microsoft.DirectX这两个dll进行录音,需要先安装microsoft directx 9.0cz这个组件, 百 ...

  8. 【Android】Android SDK Manager更新慢/失败的问题

    前言:更新下载Intel x86 Atom_64 System Image的时候总是失败,速度只有几KB,我这是10M的网啊. 最后找到一篇日志,解决了这个问题.非常感谢!其参考地址:http://w ...

  9. JavaScripts 基础详细笔记整理

    一.JS简介 JavaScript 是 Web 的编程语言,浏览器内置了JavaScript语言的解释器,所以在浏览器上按照JavaScript语言的规则编写相应代码之,浏览器可以解释并做出相应的处理 ...

  10. C#枚举类型和结构体

    注意:枚举类型和结构体都属于值类型. 结构体:就是一个自定义的集合,里面可以放各种类型的元素,用法大体跟集合一样. 一.定义的方法: struct student { public int nianl ...