初学多线程,分批处理list值的时候速度居然不如直接打印?

Hardy
  • 7

刚学习多线程,想试着用它解决处理大的集合的情况,创建一个size为900000的list,创建三个线程,每个线程打印300000个。处理的速度居然小于我用for循环直接循环打印的速度,附上代码如下:

package com.practice.concurrent.dealwithlist;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


public class DealWithListTest {

  private static ExecutorService executorService = Executors.newCachedThreadPool();
  private static Lock lock = new ReentrantLock();

  private static List<String> list = new ArrayList<String>();

  static {
    for (int i=0; i<900000; i++){
      list.add(i + "str");
    }
  }

  //分三段打印数据
  private static void dealwith(int i){
    lock.lock();
    try{
      List<String> childList = list.subList(i*300000 , (i+1)*300000);
      for (String s : childList){
        System.out.println(Thread.currentThread() + "------------" + s);
      }
    }finally {
      lock.unlock();
    }

  }

  private static class Task implements Runnable{

    private int i;

    public Task(int i){
      this.i = i;
    }

    @Override
    public void run() {
      dealwith(i);
    }
  }

  public static void main(String[] args) {
    long before = System.currentTimeMillis();
    /**
     * 使用多线程耗时2830毫秒
     */
    for (int i = 0; i <3 ; i++) {
      executorService.execute(new Task(i));
    }
    executorService.shutdown();
    while (true){
      if (executorService.isTerminated()){
        System.out.println("所有线程执行结束");
        break;
      }
    }
    /**
     * 直接打印耗时2102毫秒
     */
//    for (String s : list){
//      System.out.println(s);
//    }
    System.out.println("整体耗时" + (System.currentTimeMillis()-before) + "毫秒");
  }

}

一定是我对线程的理解不够深入,希望大家给予批评指正。

回复
阅读 6.1k
4 个回答

上面程序里我主要有以下几个问题:
1.lock并没有任何作用,因为这里不涉及到共享资源的争夺,list我是切分为完全不同的三段来处理的。
2.如楼上几位朋友所说,system.out.println()是同步方法,开销较大。
3.如@Ezio一楼所说,多线程多用于处理计算密集型操作。所以这里的打印是不应该的。

所以我做了如下更改,其中
1.我把打印操作改为了“为list里的每个对象后面添加一个字符串”;
2.list里的元素由String改为StringBuffer类型,因为String对象是不可修改的,每次修改其实都是废弃了原对象,建立了新对象,也是比较消耗时间的。
新代码如下:

package com.practice.concurrent.dealwithlist;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class DealWithListTest {

  private static ExecutorService executorService = Executors.newCachedThreadPool();

  /**
   * 非线程安全
   */
  private static List<StringBuffer> list = new ArrayList<StringBuffer>();

  static {
    for (int i=0; i<9000000; i++){
      list.add(i, new StringBuffer("str"));
    }
  }

  //分三段打印数据
  private static void dealwith(int i){

      for (int j=i*3000000; j<(i+1)*3000000; j++){
        list.set(j,list.get(j).append("--add"));
      }
  }

  private static class Task implements Runnable{

    private int i;

    public Task(int i){
      this.i = i;
    }

    @Override
    public void run() {
      dealwith(i);
    }
  }

  public static void main(String[] args) {
    long before = System.currentTimeMillis();
    /**
     * 使用多线程处理耗时148毫秒
     */
    for (int i = 0; i <3 ; i++) {
      executorService.execute(new Task(i));
    }
    executorService.shutdown();
    while (true){
      if (executorService.isTerminated()){
        break;
      }
    }
    /**
     * 单线程处理耗时288毫秒
     */
//    for (int i=0; i<list.size(); i++){
//      list.set(i,list.get(i).append("--add"));
//    }
    System.out.println(list.get(0) + "整体耗时" + (System.currentTimeMillis()-before) + "毫秒");
  }

}

这里list的size改为了9000000,但是最终的处理结果居然从几千毫秒到 一百多 二百多毫秒。好刺激!
对多线程处理的测试结果也比较乐观
线程处理 148毫秒
单线程处理 288毫秒

如有理解问题,望指正。

打印是IO密集操作,用多线程还增加了线程切换的耗时……
多线程应该用来做计算密集的操作

你这ReentrantLock是用来做什么的?为什么要要求抢占锁的线程释放锁其他线程才能执行?println本来就是同步方法多个线程只会徒增等待。

你加上ReentrantLock相当于单线程执行了,每个线程都要等待其它线程释放了锁才能执行。
另外,System.out.println的开销相对比较大的,是程序里的主要瓶颈

宣传栏