进程
通俗的说,进程就是一个程序运行起来的状态。一般情况下,一个程序运行起来之后,都会在操作系统中对应一个进程。
从操作系统的角度来说,进程是操作系统进行资源分配的基本单位。
线程
线程是CPU进行任务调度和执行的基本单位。
一个进程往往包含多个线程。
在Linux系统下是没有线程的概念的,它是用进程模拟的线程, 一般是某个主进程的子进程,因此把线程叫做轻量级进程。
线程的状态(Java)
NEW: 新创建了一个线程对象,但还没有调用start()方法。 Thread t = new Thread();
RUNNABLE: Java线程中将就绪(ready)和运行中(running)两种状态笼统的成为“运行” t.start()
BLOCKED : 线程阻塞于锁, 只有synchronized才有这种状态,因为synchronized锁会经过CPU调度。
WAITING : 等待 t.wait()
TIME_WAITING: 该状态不同于WAITING,它可以在指定的时间内自行返回 t.sleep(2000);
TERMINATED: 线程执行完毕
下面的代码表示了t1线程对应的不同状态:
Object o = new Object();
Thread t1 = new Thread(() -> { // NEW
synchronized (o) { //BLOCKED
try {
Thread.sleep(2000); //TIME_WAITING
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});// TERMINATED
Thread t2 = new Thread(() -> {
try {
t1.wait(); // WAITING
t1.notify(); // RUNNABLE
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.start(); // RUNNABLE 当没有抢夺到时间片时,为READY, 当被CPU调度执行时,状态为RUNNING
t2.start();
t2.join();
纤程
纤程又叫协程,可以看作是轻量级线程。
之所以称为轻量级线程,是因为它不需要由CPU来进行调度,与线程相比,纤程的切换损耗要小得多。
在一个线程内可以开启很多纤程,这些纤程只需要由当前线程来进行调度。
另一个角度来讲,它可以看作是用户态的线程。
纤程VS线程
目前Java的官方JDK中还没有支持创建纤程,对纤程的支持还在尝试阶段,为了实验纤程的效率,我们暂时先引入第三方lib对纤程的支持
场景:我们使用5W个线程来执行,每个线程中循环100次对count值加1,因为count 是AtomicInteger修饰的,所以是线程安全的。那么这5W个线程执行完后,count的值应为5000000。
使用线程的方式:
public class UseThread {
static AtomicInteger count = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
Thread[] threads = new Thread[50000];
long start = System.currentTimeMillis();
for (int i = 0 ; i < threads.length; i++) {
threads[i] = new Thread(() -> add());
threads[i].start();
}
for (int i = 0 ; i < threads.length; i++) {
threads[i].join();
}
System.out.println("count :" + count.get());
System.out.println(System.currentTimeMillis() - start);
}
static void add() {
for (int i = 0; i < 100; i++) {
count.getAndIncrement();
}
}
}
执行结果, 耗时:2370ms
接下来使用纤程的方式:
目前Java的官方JDK中还没有支持创建纤程,对纤程的支持还在尝试阶段,为了实验纤程的效率,我们暂时先引入第三方lib对纤程的支持
POM文件:
<dependency>
<groupId>co.paralleluniverse</groupId>
<artifactId>quasar-core</artifactId>
<version>0.8.0</version>
</dependency>
public class UseFiber {
static AtomicInteger count = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException, ExecutionException {
Fiber[] fibers = new Fiber[50000];
long start = System.currentTimeMillis();
for (int i = 0 ; i < fibers.length; i++) {
fibers[i] = new Fiber(() ->add());
fibers[i].start();
}
for (int i = 0 ; i < fibers.length; i++) {
fibers[i].join();
}
System.out.println("count :" + count.get());
System.out.println(System.currentTimeMillis() - start);
}
static void add() {
for (int i = 0; i < 100; i++) {
count.getAndIncrement();
}
}
}
执行结果, 耗时328ms
由此我们可以看到,在某些场景下,协程(纤程)的效率要高于线程。
协程的上下文切换是在用户空间,调度的成本相对较低。但是并不是协程一定比线程高效。
一般情况下,我们可以这样认为:
对于IO密集型的任务,协程更高效。(因为绝大部分时间都是在等待 IO)
对于CPU密集型的任务,线程更高效。