订阅博客
收藏博客
微博分享
QQ空间分享

java线程池和创建java线程性能比较

频道:技术分享 标签:菜鸟程序员Java-SpringMVC 时间:2016年04月14日 浏览:1720次 评论:0条

前面几篇文章我们一直在讲述java线程池的相关知识点,我相信现在大家对java多线程一定有了一些了解,jdk中的核心实现类为java.util.concurrent.ThreadPoolExecutor,前面大家了解到它的原理(可查看:http://cuiyongzhi.com/index.php/post/72.html  ),看过它的源码(可查看:http://cuiyongzhi.com/index.php/post/71.html  );但是就像我一样,大家可能对它的作用存在误解;现在问题来了,jdk为什么要提供java线程池?使用java线程池对于每次都创建一个新Thread有什么优势?下面我们就一一来验证这些我们心中的疑虑!

很长一段时间里我一直以为java线程池是为了提高多线程下创建线程的效率。创建好一些线程并缓存在线程池里,后面来了请求(Runnable)就从连接池中取出一个线程处理请求;这样就避免了每次创建一个新Thread对象。直到前段时间偶尔我看到Neal Gafter(和Joshua Bloch合著了《Java Puzzlers》,现任职于微软,主要从事.NET语言方面的工作)的访谈中有提到下面这样一句话:

1.png
乍一看,大神的思路就是不一样:java线程池是为了防止java线程占用太多资源?Java大神说占资源,那么我们就来看看是不是这么玩的!

①在验证大牛的占用资源的观点之前先验证下我的理解:java线程池和创建java线程哪个效率高?下面直接上测试代码:

package com.cuiyongzhi.io;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import junit.framework.TestCase;

/*
 * java线程对比
 */
public class ThreadPoolTest extends TestCase {
    private static final int COUNT = 10000;

    /**
     * @Description: java线程池实现
     * @param @throws InterruptedException   
     * @author dapengniao
     * @date 2016年4月14日 上午10:16:23
     */
    public void testThreadPool() throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(COUNT);
        ExecutorService executorService = Executors.newFixedThreadPool(100);
        long bg = System.currentTimeMillis();
        for (int i = 0; i < COUNT; i++) {
	    Runnable command = new TestRunnable(countDownLatch);
	    executorService.execute(command);
        }
        countDownLatch.await();
        System.out.println("testThreadPool:" + (System.currentTimeMillis() - bg));
    }

    /**
     * @Description: 创建java线程实现
     * @param @throws InterruptedException   
     * @author dapengniao
     * @date 2016年4月14日 上午10:16:31
     */
    public void testNewThread() throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(COUNT);
        long bg = System.currentTimeMillis();
        for (int i = 0; i < COUNT; i++) {
	    Runnable command = new TestRunnable(countDownLatch);
	    Thread thread = new Thread(command);
	    thread.start();
        }
        countDownLatch.await();
        System.out.println("testNewThread:" + (System.currentTimeMillis() - bg));
    }

    private static class TestRunnable implements Runnable {
        private final CountDownLatch countDownLatch;

        TestRunnable(CountDownLatch countDownLatch) {
	    this.countDownLatch = countDownLatch;
        }

        @Override
        public void run() {
	    countDownLatch.countDown();
        }
    }
}

在代码中我们添加了计时,这里使用Executors.newFixedThreadPool(100)是为了控制线程池的核心连接数和最大连接数一样大,都为100,我的机子上的测试结果:

2.png

可以看到,使用线程池处理10000个请求的处理时间为45ms,而每次启用新线程的处理时间为939ms,好了,使用线程池确实要比每次都创建新线程要快一些;但是如果按照大牛的说法这点时间应该根本不算什么,所以下面我们来验证下大牛的资源占用问题!

②java线程池是为了节约资源?再来测试代码如下:

package com.cuiyongzhi.io;

import junit.framework.TestCase;

/**
 * ClassName: ThreadPoolTestTwo
 * @Description: 用java不断创建新线程,查看jvm内存变化
 * @author dapengniao
 * @date 2016年4月14日 上午10:53:08
 */
public class ThreadPoolTestTwo extends TestCase {
    public void testThread() throws InterruptedException {
        int i = 1;
        while (true) {
	    Runnable command = new TestRunnable();
	    Thread thread = new Thread(command);
	    thread.start();
	    System.out.println(i++);
        }
    }

    private static class TestRunnable implements Runnable {
        @Override
        public void run() {
	    try {
	        Thread.sleep(100000);
	    } catch (InterruptedException e) {
	        e.printStackTrace();
	    }
        }
    }
}

当我们运行代码的时候我么查看jvm内存变化,我们会发现会追随者线程数增多而jvm上涨,停下来jvm也跟着停下来,或许机器弱点的话可能就会OOM(java.lang.OutOfMemoryError: unable to create new native thread)了,运行过程中如下:

3.gif

至于为什么会抛OOMError?因为jvm会为每个线程分配一定内存(JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K,也可以通过jvm参数-Xss来设置),所以当线程数达到一定数量时就报了该error!设想如果不使用java线程池,而为每个请求都创建一个新线程来处理该请求,当请求量达到一定数量时一定会内存溢出的;而我们使用java线程池的话,线程数量一定会<=maximumPoolSize(线程池的最大线程数),所以设置合理的话就不会造成内存溢出!

所以执行一个异步任务你还只是使用new Thread,那你就out太多了,最后总结下:

A.new Thread的弊端

  • 每次new Thread新建对象性能差。

  • 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。

  • 缺乏更多功能,如定时执行、定期执行、线程中断。

B.Java提供的四种线程池的好处

  • 重用存在的线程,减少对象创建、消亡的开销,性能佳。

  • 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。

  • 提供定时执行、定期执行、单线程、并发数控制等功能。


                                        ----------本文来源于网络学习,感谢你的翻阅,如有疑问可以留言讨论!


◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

分享:

支付宝

微信