对多线程与串行任务的效率探究

对多线程与串行任务的效率探究

    通常,我们选择多线程执行任务有两个理由,一是复杂任务采用多线程处理能够在发生并发时让用户减少等待也能防止阻塞,一是充分利用空闲时间,提高任务处理的效率,就后者而言,此处探讨不考虑客户端并发是否有必要把一个任务拆分成多线程来处理。

    为了探究多线程的效率问题,我做了一个实验,将不同种类的任务分别用单线程和多线程执行,同时也试验了不同种类的锁机制。测试基于Java 8的版本。

    任务一  循环打印

    开启五个线程执行任务,设定了足够次数的循环输出,输出的数字和当前线程,利用System.currentTimeMillis()统计任务用时。(代码略)以下是相同任务在不同环境下执行多次的平均执行时间。

    

无锁执行 9943
synchronized(new Object()) 9923
synchronized("lock") 6864
相同的ReentrantLock() 6682
new ReentrantLock() 10220
串行执行 6782

    其实,由于整段代码都加了锁,锁不同的对象就相当于串行执行,此处执行print任务利用多线程执行反而使效率降低了,因为print过程中cpu几乎没有空闲时间,而线程切换引起的上下文切换却需要很大的时间耗费,所以此处不应采用多线程处理任务。

    任务二  读取文件

    读取不同的文件测试效率,文件大小约为7M。

    Util:

public static String getStr(String filePath) throws FileNotFoundException {
        return getStr(new FileInputStream(filePath),"<br>");
}

public static String getStr(InputStream in,String Enter)  {
    BufferedReader reader = null;
    StringBuffer sb = new StringBuffer();
    try {
        reader = new BufferedReader(new InputStreamReader(in, "UTF-8"));
        String s;
        while((s=reader.readLine()) != null){
            sb.append(s+Enter);
        }
        reader.close();
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return sb.toString();
}

    Test:

public class MainApplicationTests {
    class MyTh implements Runnable{
        //Lock lock ;
        Integer n;
        MyTh(Integer n){
            this.n=n;
        }
        @Override
        public void run() {
            int i=0;
            synchronized(new Object()) {
                try {
                    String s = FileUtil.getStr("D://xxx//xxx" + n + ".zip");
                    System.out.println(s.substring(10, 20));
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Test
    public void test() throws InterruptedException, FileNotFoundException {
        Long q=System.currentTimeMillis();
        //Thread t=Thread.currentThread();
        //ReentrantLock lock = new ReentrantLock();

        Thread n=new Thread(new MyTh(1));
        Thread n1=new Thread(new MyTh(2));
        Thread n2=new Thread(new MyTh(3));
        Thread n3=new Thread(new MyTh(4));
        Thread n4=new Thread(new MyTh(5));
        n.start();
        n1.start();
        n2.start();
        n3.start();
        n4.start();
        n.join();
        n1.join();
        n2.join();
        n3.join();
        n4.join();

        System.out.println(System.currentTimeMillis()-q);
        System.out.println(q);
    }
}

执行时间:

多线程执行 24327
串行执行 17444

    IO的开启确实有等待时间,此处后面探究。

    任务三  HttpClient请求接口

    接口访问是典型需要等待结果的,我们利用多线程和单线程访问一个约需2秒的接口。

执行时间:

多线程执行 2587
串行执行 7238

    这种情境下,多线程的性能极大的发挥了出来,利用线程空闲的等待请求返回时间,可以切换其他线程进行任务。

疑惑:多核?

    JVM会将多线程任务分发到多个核心上

//TODO


2019-06-11鱼鱼

{{blog.title}}

创建于 {{blog.createTimeStr}}   created  by  {{blog.author}} {{tag}}
最后修改于 {{blog.timelineStr}}
修改文档