再谈并发
上一篇python并发中讲到了,使用多进程,多线程,多任务来加快程序的运行。其中讲到的一点似乎有点问题,操作系统中线程是调度器的最小执行单位,那为何python中的多线程无法利用多核,只能在一个处理器上运行呢?因为python中有GIL(全局解释器锁)这么个东西,当然它只是cpython解释器的一个特性,其他解释器可能没有。
大学时总在想,学操作系统、计算机组成原理、数据结构有啥用啊?还不如学习如何使用hibernate
、struts
、spring
。当你工作3年后,对如何使用这些工具再也提不起兴趣时或者想深入了解它到底为什么是那样时,你就会重新回过头来打量大学时学的这些底层知识。
也许你对下面一句话耳熟能详:
进程是资源分配的最小单位,线程是最小执行单位。
大学时读的完全是字面意思啊?并没有思考什么是进程,为何要搞出来个进程?好吧,下面又是我杜撰的。
进程是一个操作系统级别的概念,运行一个程序时往往需要各种资源,操作系统把一个程序以及运行时所需要的资源抽象成一个进程,这里的资源是存储资源和计算资源。各个进程的计算资源是由操作系统的调度器统一分配的,而不是一个进程永远霸占计算资源;因为进程使用的是虚拟内存地址,不同进程的同一虚拟地址可能映射到了不同的物理内存上,所以不同进程间的存储资源是不共享的。
因为进程的储存资源不共享,创建销毁和切换的开销比较大。所以出现了轻量级进程,即线程。线程是共享同一进程的存储资源的,一个进程可以创建若干线程。同时它也是调度器的最小执行单元,可见多线程是能利用多核处理器的。
java没有操作进程并发的类,官方暂时也不支持协程,但是有一些第三方库,比Quasar。下面是多线程的几种方式:
- 通过
ThreadPoolExecutor
- 通过
CompletableFuture
- 通过流的并行处理
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.concurrent.*;
import java.util.stream.IntStream;
public class ThreadPoolDemo {
static String content = "1234567890abcdefghijklmnopqrstuvwxyz";
static int size = 400;
public static void a(){
int cnt = Runtime.getRuntime().availableProcessors() * 2 + 1;
ExecutorService threadPoolExecutor = Executors.newFixedThreadPool(cnt);
ArrayList<Future<?>> fs = new ArrayList<>(size);
long start = System.currentTimeMillis();
for(int i = 0; i < size; ++i){
int seq = i;
Future<?> f = threadPoolExecutor.submit(() -> createFile("a", seq));
fs.add(f);
}
for(Future<?> f : fs){
try {
f.get();
} catch (InterruptedException|ExecutionException e) {
e.printStackTrace();
}
}
System.out.println(String.format("%s = %s", "a", (System.currentTimeMillis() - start)));
threadPoolExecutor.shutdown();
}
public static void b(){
CountDownLatch countDownLatch = new CountDownLatch(size);
long start = System.currentTimeMillis();
for(int i = 0;i<size;++i){
int seq = i;
CompletableFuture
.runAsync(()->createFile("b", seq))
.whenComplete((r, e)->{
countDownLatch.countDown();
});
}
try {
countDownLatch.await();
System.out.println(String.format("%s = %s", "b", (System.currentTimeMillis() - start)));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void c(){
long start = System.currentTimeMillis();
IntStream.range(0, size)
.parallel()
.forEach(e -> {
createFile("c", e);
});
System.out.println(String.format("%s = %s", "c", (System.currentTimeMillis() - start)));
}
public static void createFile(String prefix, int name){
File file = new File("D:/files/" + prefix + "_" + name);
if(!file.exists()){
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(content.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
// a();
//b();
c();
}
}