提升--01---线程基础知识
Posted 高高for 循环
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了提升--01---线程基础知识相关的知识,希望对你有一定的参考价值。
线程的历史
- 之前的硬件,只有一个CPU
- 之前的OS,只运行一个进程
- 随着多核CPU的出现,人们开始追求对CPU效率的极致压榨
- 多线程的程序随之诞生,但随之诞生的,也是非常难以应对的各种并发bug
进程 线程
多线程–01–程序、进程、线程
纤程/协程:绿色线程,用户管理的(而不是OS管理的)线程
- 什么是进程:资源分配的基本单位(静态概念)
- 什么是线程:资源调度的基本单位(动态概念)
- 通俗说:一个程序中不同的执行路径
线程的调度,切换线程也存在性能损耗
区分:
- 程序:可执行文件,如xx.exe
- 进程:资源分配的最小单位。一个程序可能由多个进程组成,进程启动后,操作系统会在内存中给进程分配空间
- 线程:程序调度执行的最小单位,多个线程共享进程中的资源
- 单线程:只有一条执行路径,一个执行任务
- 多线程:有多条执行路径在同时运行,多个执行任务
一个java应用程序
一个java应用程序java.exe ,其实至少有三个线程:当然如果发生异常,会影响主线程
- main线程
- gc()垃圾回收线程
- 异常处理线程
一个多线程程序一个进程,会先运行主线程,然后运行期间,再进行线程的切换,抢夺时间片.
1. 单核CPU设定多线程是否有意义
回答这个问题需要考虑,我们的线程被用来执行什么样的任务? 常见的任务分为两种:
- CPU密集型任务
- IO密集型任务
CPU密集型任务(CPU-bound):
- 在一个任务中,主要做计算,CPU持续在运行,CPU利用率高,具有这种特点的任务称为CPU密集型任务。
IO密集型任务(IO-bound):
- 在一个任务中,大部分时间在进行I/O操作,由于I/O速度远远小于CPU,所以任务的大部分时间都在等待IO,CPU利用率低,具有这种特点的任务称为IO密集型任务。
因为存在线程等待的情况,所以在等待的时间,即是是单核cpu,也可以进行线程切换,让其他线程继续用cpu,这样提高了cpu的利用率.
2. 工作线程数是不是设置的越大越好?
案例:
- 测试设备:AMD r7 4700u , 8核
需求 :求一个亿的随机数据累加的和.
public class T01_MultiVSSingle_ContextSwitch {
//=====================一亿数据累积求和 ==============================
private static double[] nums = new double[1_0000_0000];
private static Random r = new Random();
private static DecimalFormat df = new DecimalFormat("0.00");
static {
for (int i = 0; i < nums.length; i++) {
nums[i] = r.nextDouble();
}
}
public static void main(String[] args) throws Exception {
m1();
m2();
m3();
}
}
单线程方法: m1
private static void m1() {
long start = System.currentTimeMillis();
double result = 0.0;
for (int i = 0; i < nums.length; i++) {
result += nums[i];
}
long end = System.currentTimeMillis();
System.out.println("m1: 1个线程" + (end - start) + " result = " + df.format(result));
}
2个线程 方法: m2
static double result1 = 0.0, result2 = 0.0, result = 0.0;
private static void m2() throws Exception {
Thread t1 = new Thread(() -> {
for (int i = 0; i < nums.length / 2; i++) {
result1 += nums[i];
}
});
Thread t2 = new Thread(() -> {
for (int i = nums.length / 2; i < nums.length; i++) {
result2 += nums[i];
}
});
long start = System.currentTimeMillis();
t1.start();
t2.start();
t1.join();
t2.join();
result = result1 + result2;
long end = System.currentTimeMillis();
System.out.println("m2: 2个线程" + (end - start) + " result = " + df.format(result));
}
32个线程 方法: m3
private static void m3() throws Exception {
final int threadCount = 32;
Thread[] threads = new Thread[threadCount];
double[] results = new double[threadCount];
final int segmentCount = nums.length / threadCount;
CountDownLatch latch = new CountDownLatch(threadCount);
for (int i = 0; i < threadCount; i++) {
int m = i;
threads[i] = new Thread(() -> {
for (int j = m * segmentCount; j < (m + 1) * segmentCount && j < nums.length; j++) {
results[m] += nums[j];
}
latch.countDown();
});
}
double resultM3 = 0.0;
long start = System.currentTimeMillis();
for (Thread t : threads) {
t.start();
}
latch.await();
for (int i = 0; i < results.length; i++) {
resultM3 += results[i];
}
long end = System.currentTimeMillis();
System.out.println("m3: 32个线程" + (end - start) + " result = " + df.format(result));
}
测试:
结论:
- 多线程通常情况下比单线程效率高
- 线程并不是越多越好,因为线程切换也需要耗费系统资源和时间.
3. 线程池中的线程数量设置多少合适?
根据任务类型推断:
当我们需要初始化一个线程池时,就要考虑我们的线程池被用来执行什么样的任务。
所以我们在设计线程池时,应先对执行的任务有个大体分类,然后根据类型进行设置。一般而言,两种任务的线程数设置如下:
CPU密集型任务:
- 线程个数为CPU核数。这几个线程可以并行执行,不存在线程切换到开销,提高了cpu的利用率的同时也减少了切换线程导致的性能损耗
IO密集型任务:
- 线程个数为CPU核数的两倍。到其中的线程在IO操作的时候,其他线程可以继续用cpu,提高了cpu的利用率
合适线程计算公式:
实际的w/c等待时间和计算时间的比率,需要实际生产压测出来.运用相关检测工具.
性能分析工具 profile
不管哪种公式和什么类型的任务,都需要实际生产压测数据为参考.
以上是关于提升--01---线程基础知识的主要内容,如果未能解决你的问题,请参考以下文章