并发流(forkjoin)你会用吗?VS线程池,哪个好

背景:

线上代码,使用了JDK8的 lambda表达式 并发流,压测吞吐量一直上不去,后期改了传统方式线程池,就可以了,所以写了一个demo测试之:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.IntStream;

import org.springframework.util.StopWatch;

import cn.cmvideo.aspirin3.ng.common.exception.BusinessException;

/**
 * 线程池 VS 并发流 demo:   线程池 16个线程
 * 任务量     业务时间     线程池耗时(s)   并发流耗时(s)
 * 100      100       0.9261942    0.767432
 * 100      140       1.0180679    1.0154289
 * 100      200       1.0796075    1.4417255
 * 1000     100       11.418968    7.0394389
 * 1000     160       11.4577861   11.0552394
 * 1000     200       11.6815915   13.0941389
 * 1000     500       15.7660305   31.8282304
 * 5000     100       59.4376765   34.5796238
 * 5000     160       58.4465729   54.2535337
 * 5000     170       51.4519155   59.2798839
 * 5000     200       60.6012208   64.0964843
 * 5000     500       77.085486    158.6610821
 * 10000    20        118.4741302  19.7266118
 * 10000    200       120.0732598  128.0488622
 * 50000    1         602.1516255  3.8944299
 * @author douzi
 * @date 2024-05-17
 */
public class Test {
	public static void main(String[] args) {
		MyThreadPool myPool = MyThreadPool.getInstance();
		ThreadPoolExecutor executor = myPool.getSinglePool();
		
		// 模拟任务量
		int end = 10000;
		// 模拟业务耗时
		int serviceTime = 200;
		
		System.out.println("线程池 VS 并发流:");
		StopWatch sw = new StopWatch();
		sw.start();
        IntStream.rangeClosed(0, end).parallel().forEach(i -> {
        	try {
				Thread.sleep(serviceTime);
//				System.out.println(i + " sleep后 " + Thread.currentThread().getName());
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
        });
		sw.stop();
		System.out.println("并发流耗时:" + sw.getTotalTimeSeconds());
		
		StopWatch sw1 = new StopWatch();
		sw1.start();
		List<CompletableFuture<Void>> futures = new ArrayList<>();
        for (int i = 0; i < end; i++) {
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
            	try {
					Thread.sleep(serviceTime);
//					System.out.println("sleep后 " + Thread.currentThread().getName());
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
            }, executor);
            futures.add(future);
        }
        CompletableFuture<Void> allFutures = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
        try {
			allFutures.get();
		} catch (Exception e) {
			throw new BusinessException("1","引擎匹配异常");
		}
        
        sw1.stop();
		System.out.println("线程池耗时:" + sw1.getTotalTimeSeconds());
	}
}

MyThreadPool代码:可以使用传统方式

import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import lombok.extern.slf4j.Slf4j;

/**
 * 单例线程池小工具,轻量级使用,可以在小项目中通过一两句话使用线程池。
 * 使用方式:
 * 1. MyThreadPool myPool = MyThreadPool.getInstance();
 * 2. ThreadPoolExecutor executor = myPool.getSinglePool(); 多次调用获取相同线程池,可以系统中只有一个
 * 	  ThreadPoolExecutor executor = myPool.getSinglePool(10); 多次调用获取相同线程池,可以系统中只有一个
 * 3. ThreadPoolExecutor executor = myPool.getPool(); 获取新的线程池
 * @author douzi
 */
@Slf4j
public class MyThreadPool {
	private final String DEFAULT_THREAD_NAME = "DOUZI";
	private final String DEFAULT_THREAD_GROUP_NAME = "GDZ";
	private final int DEFAULT_CORE_POOL_SIZE = 16;
	private final int DEFAULT_MAX_POOL_SIZE =  2 * Runtime.getRuntime().availableProcessors() + 1;
	private final int DEFAULT_KEEP_ALIVE_SECONDS = 60;
	private final int DEFAULT_QUEUE_CAPACITY = 5;
	/**
     * 当溢出时阻塞到队列任务数下降到队列总大小的百分比
     */
    private static final float OVERFLOW_WAIT = 0.8f;

    /**
     * 默认线程池活跃的与最大数量的比例
     */
    private static final float MAX_CORE_SIZE_RATIO = 0.5f;
	
	private ThreadPoolExecutor singlePool = null;
	
	private MyThreadPool() {
		this.singlePool = getPool();
	}
	
	private static class HolderClass {
		private static final MyThreadPool POOL = new MyThreadPool();
	}
	
	public static MyThreadPool getInstance() {
		return HolderClass.POOL;
	}
	
	public ThreadPoolExecutor getPool() {
		return getPool(DEFAULT_THREAD_NAME);
	}
	
	public ThreadPoolExecutor getPool(String threadName) {
		return getPool(threadName, DEFAULT_CORE_POOL_SIZE, DEFAULT_MAX_POOL_SIZE, DEFAULT_QUEUE_CAPACITY);
	}
	
	public ThreadPoolExecutor getPool(int maxPoolSize) {
		int corePoolSize = (int) (maxPoolSize * MAX_CORE_SIZE_RATIO);
        corePoolSize = corePoolSize == 0 ? 1 : corePoolSize;
		return getPool(DEFAULT_THREAD_NAME, corePoolSize, maxPoolSize, DEFAULT_QUEUE_CAPACITY);
	}
	
	public ThreadPoolExecutor getPool(int maxPoolSize, int queueCapacity) {
		int corePoolSize = (int) (maxPoolSize * MAX_CORE_SIZE_RATIO);
        corePoolSize = corePoolSize == 0 ? 1 : corePoolSize;
		return getPool(DEFAULT_THREAD_NAME, corePoolSize, maxPoolSize, queueCapacity);
	}
	
	public ThreadPoolExecutor getPool(int corePoolSize, int maxPoolSize, int queueCapacity) {
		return getPool(DEFAULT_THREAD_NAME, corePoolSize, maxPoolSize, queueCapacity);
	}
	
	public ThreadPoolExecutor getPool(String threadName, int corePoolSize, int maxPoolSize, int queueCapacity) {
		ThreadGroup groupName = new ThreadGroup(DEFAULT_THREAD_GROUP_NAME);
		ThreadFactory namedThreadFactory = new DefaultThreadFactoryImpl(threadName, groupName);
		
        return new ThreadPoolExecutor(corePoolSize, maxPoolSize, DEFAULT_KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(queueCapacity), namedThreadFactory, 
                new DefaultRejectedHandler(threadName, queueCapacity));
	}
	
    public ThreadPoolExecutor getSinglePool() {
		return singlePool;
	}
    
	/**
     * 关闭线程池(阻塞)
     *
     * @param executor 线程池
     */
    public void poolStopSync(ExecutorService executor) {
        if (executor != null && !executor.isShutdown()) {
            executor.shutdown();
            try {
                while (!executor.awaitTermination(2, TimeUnit.SECONDS)) {
                    log.debug("wait for the thread pool task to end.");
                }
            } catch (InterruptedException e) {
                log.error("close pool is error!", e);
                Thread.currentThread().interrupt();
            }
        }
    }
    
    /**
     * 立即关闭线程池
     *
     * @param executor 线程池
     */
    public void poolStopSyncNow(ExecutorService executor) {
        if (executor != null && !executor.isShutdown()) {
        	executor.shutdownNow();
        }
    }

    /**
     * 关闭线程池(非阻塞)
     *
     * @param executor 线程池
     */
    public void poolStopAsync(ExecutorService executor) {
        new Thread(() -> poolStopSync(executor)).start();
    }
    
    class DefaultThreadFactoryImpl implements ThreadFactory {
        private final String namePrefix;
        private final ThreadGroup group;
        private final AtomicLong count;

        DefaultThreadFactoryImpl(String namePrefix, ThreadGroup group) {
            this.namePrefix = namePrefix;
            this.group = group;
            this.count = new AtomicLong();
        }

        DefaultThreadFactoryImpl(String namePrefix) {
            this(namePrefix, (ThreadGroup) null);
        }

        @Override
        public Thread newThread(Runnable target) {
            return new Thread(this.group, target, this.namePrefix + "-" + this.count.incrementAndGet());
        }
    }
    
    class DefaultRejectedHandler implements RejectedExecutionHandler {
    	private int queueCapacity;
    	private String threadName;
    	
    	DefaultRejectedHandler(String threadName, int queueCapacity) {
    		this.threadName = threadName;
    		this.queueCapacity = queueCapacity;
    	}
    	
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        	try {
                while (executor.getQueue().size() > queueCapacity * OVERFLOW_WAIT) {
                    Thread.sleep(50L);
                }
            } catch (InterruptedException e) {
                log.error(threadName + ":sleep is false", e);
                Thread.currentThread().interrupt();
            }
            executor.submit(r);
        }
    }
}

测试结果如下:

任务量

业务时间

线程池耗时(s)

并发流耗时(s)

100

100

0.9261942

0.767432

100

140

1.0180679

1.0154289

100

200

1.0796075

1.4417255

1000

100

11.418968

7.0394389

1000

160

11.4577861

11.0552394

1000

200

11.6815915

13.0941389

1000

500

15.7660305

31.8282304

5000

10058.5362519

34.5796238

5000

160

58.4465729

54.2535337

5000

170

51.4519155

59.2798839

5000

200

77.4818196

236.9299424

5000500

77.085486

158.6610821

10000

20

118.4741302

19.7266118

10000200

120.0732598

128.0488622

50000

1

602.1516255

3.8944299

最后总结:

从上边的测试结果可以看出,线程池比较稳定,随着任务量的增加,业务耗时的增加,呈线性增长;而并发流则不然,当业务耗时低的时候,会非常快;业务耗时高的时候,又会非常慢;所以:当我们的业务耗时比较低的时候,例如低于150毫秒,推荐使用并发流;反之则正常使用线程池。

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

窦再兴

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值