多线程对于大家来说都不陌生,简单的来说就是在一个进程运行时产生了多个线程,以此来同步完成多项任务,注意多线程不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率,线程是在同一时间需要完成多项任务的时候实现的。
本文不会过多的解释关于多线程的一些基础概念,重点将关注如何在实际项目中更好的使用多线程,因为有很多项目中可能并不需要我们使用多线程,或者说开发人员并没有注意到可以使用多线程,其次多线程当然也并不是创建的越多越好,再加上共享资源下如何保证线程安全,线程死锁等问题,这就使得我们在使用多线程时可能总是畏手畏脚。
多线程比单线程快?
首先我们要理解并不是任何场景下都需要我们使用多线程去执行任务,因为有时候多线程并不一定比单线程快。
public class Test {
public static void main(String[] args) throws InterruptedException {
Test t = new Test();
t.method();
//t.method2();
}
private void method() {
long s = System.currentTimeMillis();
for (int i = 0; i < 2000000; i++) {
System.out.println(i);
}
long e = System.currentTimeMillis();
System.out.println("method 执行结束,耗时:" + (e - s));
}
private void method2() throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(10000);
long s = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println(i);
}
countDownLatch.countDown();
}
}).start();
}
countDownLatch.await();
long e = System.currentTimeMillis();
System.out.println("method2 执行结束,耗时:" + (e - s));
}
}
一个简单的例子,单线程执行200万次打印,耗时大约5秒不到。
一万个线程,每个线程执行200次,总计也是200万次打印,耗时大约是6秒多。
如果增加到十万个线程,每个线程执行20次,耗时大约需要8秒。
所以我们可以看出并不是线程越多执行的速度就越快,实际上我们是需要考虑当多线程数量超过CPU核数时,需要通过CPU分配时间片给线程,线程才能执行,所以多个线程可能会不断的在就绪和运行状态之间来回切换,也就是所谓的上下文切换带来的性能消耗的问题。
到底创建多少个线程合适?
一般创建线程数可以根据线程执行任务的性质来确定。
任务的性质一般可以划分为:CPU密集型、IO密集型
CPU密集型:一般建议线程的数与CPU核心数保持一致。
IO密集型:一般可以设置2倍的CPU核心数的线程数,因为此类任务CPU比较空闲,可以多分配点线程充分利用CPU资源来提高效率。
通过Runtime.getRuntime().availableProcessors()可以获取CPU核心数。
另外还有一个公式可以借鉴:
线程核心数 = CPU核心数 / (1-阻塞系数)
阻塞系数 = 阻塞时间/(阻塞时间+使用CPU的时间)
用到线程时直接创建?
由于线程的创建和销毁也是有一定性能消耗的,所以我们一般推荐使用线程池技术,JDK中也为我们提供好了一套成熟的使用线程池的类,ThreadPoolExecutor,避免线程的频繁创建和销毁,关于JDK中线程池的介绍可以参考JDK线程池详解这篇文章。
项目中的使用
说了这么多,到底平时在开发时涉及到哪些地方可以考虑使用多线程呢?
异步任务
比如我们下单购物成后一般都会收到相关的信息推送,我们可以把这类信息推送的任务交给一个线程单独去处理,信息推送成功与失败都不影响主线程中的其他业务,当然有时候会使用MQ替代。
任务拆分计算并汇总
很简单的道理,多个任务如果没有相互依赖的情况下,则可以使串行变并行,并行中消耗时间最长的任务即为总消耗时间,而串行中总消耗时间为所有任务耗时之和。
比如页面需要展现某个数据,这个数据由多个数据来源汇总得到,那么可以通过多个线程同时去计算每个数据来源的结果,最后汇总后返回。
多任务并行处理
处理一些批任务的时候,也可以考虑使用多线程,比如通过定时任务每天凌晨整理一些数据的时候,可以通过多线程并行处理,提高系统的效率。