zoukankan      html  css  js  c++  java
  • 线程池创建线程的方式一定效率高吗?

    java多线程的效率就一定高吗?

    import java.util.LinkedList;

    public class ThreadPool extends ThreadGroup {

    private boolean isClosed = false; // 线程池是否关闭
    private LinkedList workQueue; // 工作队列
    private static int threadPoolID = 1; // 线程池的id

    public ThreadPool(int poolSize) {
    super(threadPoolID + "");// 指定ThreadGroup的名称
    setDaemon(true); // 继承到的方法,设置是否守护线程池
    workQueue = new LinkedList(); // 创建工作队列
    for (int i = 0; i < poolSize; i++) {
    new WorkThread(i).start(); // 创建并启动工作线程,线程池数量是多少就创建多少个工作线程
    }

    }

    /** 等待工作线程把所有任务执行完毕 */
    public void waitFinish() {
    synchronized (this) {
    isClosed = true;
    notifyAll(); // 唤醒所有还在getTask()方法中等待任务的工作线程
    }
    // activeCount() 返回该线程组中活动线程的估计值。
    Thread[] threads = new Thread[activeCount()];
    // enumerate()方法继承自ThreadGroup类,根据活动线程的估计值获得线程组中当前所有活动的工作线程
    int count = enumerate(threads);
    for (int i = 0; i < count; i++) { // 等待所有工作线程结束
    try {
    threads[i].join(); // 等待工作线程结束
    } catch (InterruptedException ex) {
    ex.printStackTrace();
    }
    }
    }

    /** 关闭线程池 */
    public synchronized void closePool() {
    if (!isClosed) {
    // 等待工作线程执行完毕
    waitFinish();
    isClosed = true;
    // 清空工作队列
    workQueue.clear();
    // 中断线程池中的所有的工作线程,此方法继承自ThreadGroup类
    interrupt();
    }
    }

    /** 向工作队列中加入一个新任务,由工作线程去执行该任务 */
    public synchronized void execute(Runnable task) {
    if (isClosed) {
    throw new IllegalStateException();
    }
    if (task != null) {
    // 向队列中加入一个任务
    workQueue.add(task);
    // 唤醒一个正在getTask()方法中待任务的工作线程
    notify();
    }
    }

    /** 从工作队列中取出一个任务,工作线程会调用此方法 */
    private synchronized Runnable getTask(int threadid)
    throws InterruptedException {
    while (workQueue.size() == 0) {
    if (isClosed) {
    return null;
    }
    System.out.println("工作线程" + threadid + "等待任务...");
    wait(); // 如果工作队列中没有任务,就等待任务
    }
    System.out.println("工作线程" + threadid + "开始执行任务...当前任务数:"
    + workQueue.size());
    // 反回队列中第一个元素,并从队列中删除
    return (Runnable) workQueue.removeFirst();
    }

    /**
    * 内部类,工作线程,负责从工作队列中取出任务,并执行
    *
    * @author sunnylocus
    */
    private class WorkThread extends Thread {
    private int id;

    public WorkThread(int id) {
    // 父类构造方法,将线程加入到当前ThreadPool线程组中
    super(ThreadPool.this, id + "");
    this.id = id;
    }

    public void run() {
    while (!isInterrupted()) { // isInterrupted()方法继承自Thread类,判断线程是否被中断
    Runnable task = null;
    try {
    task = getTask(id); // 取出任务
    } catch (InterruptedException ex) {
    ex.printStackTrace();
    }
    // 如果getTask()返回null或者线程执行getTask()时被中断,则结束此线程
    if (task == null)
    return;

    try {
    takeTimeOperation();
    // this.sleep(5000);
    task.run(); // 运行任务
    } catch (Throwable t) {
    t.printStackTrace();
    }
    }// end while
    }// end run
    }// end workThread

    // 耗时的操作,乱写了一个
    private void takeTimeOperation() {
    int i = 0;
    String test = "";
    while (i < ThreadPoolTest.TAKETIME_NUM) {
    test += "" + i;
    i++;
    }
    }

    }


    ---------------------
    测试类:


    public class ThreadPoolTest {

    //线程数
    private static final int THREAD_NUM = 2;

    //任务数
    private static final int TASK_NUM=15;

    //费时的操作的时间度
    public static final int TAKETIME_NUM=5500;

    public static void main(String[] args) throws InterruptedException {
    long beginTime = System.currentTimeMillis();
    // 创建一个有THREAD_NUM个工作线程的线程池
    ThreadPool threadPool = new ThreadPool(THREAD_NUM);
    // 休眠500毫秒,以便让线程池中的工作线程全部运行
    Thread.sleep(500);
    // 运行任务
    for (int i = 0; i <= TASK_NUM; i++) { // 创建TASK_NUM个任务
    threadPool.execute(createTask(i));
    }
    threadPool.waitFinish(); // 等待所有任务执行完毕
    threadPool.closePool(); // 关闭线程池
    long endTime = System.currentTimeMillis();
    System.out.print("****当前线程数:" +THREAD_NUM+ ",******共用时间:" + (endTime - beginTime));

    }

    private static Runnable createTask(final int taskID) {
    return new Runnable() {
    public void run() {
    // System.out.println("Task" + taskID + "开始");
    System.out.println("Hello world");
    // System.out.println("Task" + taskID + "结束");
    }
    };
    }

    }

    1、如果线程操作的是耗时,但是不耗cpu的操作时,就是ThreadPool的WorkThread中将takeTimeOperation();这行注释掉,将this.sleep(5000);开放,同样用16个任务数测试的结果可以发现,线程数的提高对系统的性能的改进是很明显的。在线程数等于任务数的时候达到最高,如果线程数高于任务数就没什么意义了。

    2、如果线程操作耗时,又耗cpu的情况,就是ThreadPool的WorkThread中将takeTimeOperation();这行开放,将this.sleep(5000);注释掉。同样用16个任务数测试的结果可以发现线程数的提高,系统的性能反而下降了,在线程数为2的时候性能最好,估计是我的cpu是双核的原因。当线程为1的时候对cpu的利用率不高,线程过大时cpu因为自身的压力已经达到瓶颈了所以此时很难提高性能,虽然提高了并发,但大家的处理速度估计都慢下来了。而且线程本身也是要开销的,当然反正用的是池技术也就是第一次创建的时候会慢一点,接下来都从池里面取的话,对性能开销是微乎其微的。不过线程是占内存的,如果线程池开太大的话有内存溢出的危险。

    3、注意:CountDownLatch异步转同步的时候,会在所有的服务中new Thread,这样做是最保险的?如果用线程池来做:固定大小线程池有可能并发数大的时候线程已用完而导致等待超时(等待超时的问题可以将线程池的线程数设置为一次任务所需要的线程数的整数倍,多少倍也就决定了并发数,这样做不好的地方也就是线程池数设置小了无法最大化利用硬件资源,线程池设置大了会在并发量小的时候浪费硬件资源)。无限线程池Executors.newCachedThreadPool()有可能导致内存全部消耗殆尽(虽然已做串化处理,但还是无法满足使用的前提:耗时较短的任务,且任务处理速度 > 任务提交速度,如果一直不回收线程创建的资源,内存会很快消耗完毕)。结论:并发执行的效率不一定比串行执行高,因为多线程在执行的时候会有个抢占CPU资源,上下文切换的过程。详细见线程的生命周期!

    思考

    1、串行执行方式对每次请求的处理需要的硬件资源是差不多的,不会出现过量消耗系统资源,那也就可以在CountDownLatch的时候使用Executors.newCachedThreadPool()来池化线程,因为第一次请求结束第二次请求来的时候,还是同样的业务逻辑。且缓存线程池中的线程空闲后只会保留60秒,而固定长度线程池中的线程会一直保留,所以反而不适用。

    2、注意缓存线程池的使用前提,对于长时间耗时耗CPU的计算并不适合,反而直接new Thread更加适合,详见:为什么要使用线程池线程池的意义和生命周期

    3、缓存线程池使用场景可以用于如:request请求进入后insert流水,解析请求报文,插入串行队列。这样做可以立即返回response。

  • 相关阅读:
    [Java] [Exception]
    [Go back to REDIS]
    [Java] [内存泄露]
    [ZK] [Related Materials]
    [Scala] [Coursera]
    <zk在大型分布式系统中的应用>
    [Java] [Lock] [Synchronized VS ReentrantLock]
    [Data Structure] Tree
    投影矩阵的计算过程
    SQL Server 2012
  • 原文地址:https://www.cnblogs.com/xinxindiandeng/p/6378650.html
Copyright © 2011-2022 走看看