> 文档中心 > 【浅析进程、线程、纤程】

【浅析进程、线程、纤程】

浅析进程、线程、纤程

      • 进程、线程、纤程:

进程、线程、纤程:

概念:
进程:一个运行的程序
线程:一个运行程序中的执行队列,一个进程有多个线程
纤程:一般指线程中线程;线程启动时调用的是内核空间,纤程启动时调用的是用户空间。线程之间的切换是重量级的,消耗资源较多;而纤程切换是轻量级的,消耗资源较少。线程是内核空间的多个程序流并发;纤程是用户空间的多个程序流并发。
例如:Linux分两个系统级别,一个是用户空间(user),一个是内核空间(kernel),当启动线程时需要调用内核空间所以导致线程之间的切换会比较重量级,消耗资源较多;为了提高效率,将线程挪到用户空间,消耗的资源会比较低,效率比较高。这就是纤程。每个纤程都有自己的栈,其切换也是通过栈来切换的。
总结:纤程是用户态的线程,是线程中的线程。切换和调度不需要经过操作系统,轻量级的线程;JAVA一直到JDK13都不直接支持纤程,如果要⽤纤程就需要通过quasar类库来实现

Java中启动线程的三种方式:
第一种:通过继承Thread类:
通过继承Thread类来创建并启动多线程步骤如下:
1、定义Thread类的子类,重写该类的run()方法,run()方法方法体就表示该线程需要完成的任务,因此,把run()方法称为线程执行体;
2、创建子类线程对象,通过对象调用父类的start()方法启动线程.
创建子类:

public class Thread1 extends Thread{@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName()+"执行"+i);}}}

创建测试类Main:

public class Main {public static void main(String[] args) {new Thread1().start();for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName()+"执行"+i);}}}

日志打印:

main执行第0Thread-0执行第0次main执行第1Thread-0执行第1Thread-0执行第2Thread-0执行第3Thread-0执行第4Thread-0执行第5Thread-0执行第6Thread-0执行第7Thread-0执行第8Thread-0执行第9次main执行第2次main执行第3次main执行第4次main执行第5次main执行第6次main执行第7次main执行第8次main执行第9

总结:mian线程和thread-0线程在不规则循环交替执行。在main函数中我们创建了此线程的实例对象,并通过start方法启动了这个线程,执行时大家可以看到线程是以抢占式的方式运行。虽然只创建了1个线程实例,实际上共有2个线程在运行,还有main方法代表的主线程的线程执行体。
拓展:
1、Thread.currentThread(),是Thread类的静态方法,该方法总是返回当前正在执行的线程对象。
2、getName():该方法是Thread类的实例方法,该方法返当前正在执行的线程的名称。在默认情况下,主线程的名称为main,用户启动的多线程的名称依次为Thread-0,Thread-1,Thread3…Thread-n等。

第二种:通过实现Runnable接口
实现Runnable接口创建并启动多线程的步骤如下:
1、定义Runnable接口接口的实现类,重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
2、创建Runnable实现类的实例对象,并以此实例对象作为Thread的target来创建Thread类,该Thread对象才是真正的线程对象。
3.调用线程对象的start()方法来启动该线程。

定义Runnable接口接口的实现类:

public class Thread2 implements Runnable {@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName()+" 执行第"+i+"次");}}}

Mian类:

public class Main {public static void main(String[] args) {new Thread(new Thread2()).start();for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName()+" 执行第"+i+"次");}}}

日志打印:

main 执行第0Thread-0 执行第0次main 执行第1Thread-0 执行第1次main 执行第2Thread-0 执行第2Thread-0 执行第3Thread-0 执行第4次main 执行第3Thread-0 执行第5Thread-0 执行第6Thread-0 执行第7Thread-0 执行第8Thread-0 执行第9次main 执行第4次main 执行第5次main 执行第6次main 执行第7次main 执行第8次main 执行第9

这里尤其是注意:
main函数中名没有直接执行Thread2的run方法,而是将Thread2填入到了Thread中,使用start方法来启动。Runable实现类里包含run方法,仅仅作为线程执行体,而实际的线程对象依然是Thread实例对象,Thread为真正创建线程的对象

第三种:通过匿名内部类(线程池)启动:
匿名内部类本质上也是一个类实现了Runnable接口,重写了run方法,只不过这个类没有名字,直接作为参数传入Thread类,示例代码:

public class Main {public static void main(String[] args) {new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName()+" 执行第"+i+"次");}}}).start();for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName()+" 执行第"+i+"次");}}}

日志打印:

main 执行第0Thread-0 执行第0次main 执行第1Thread-0 执行第1次main 执行第2Thread-0 执行第2次main 执行第3Thread-0 执行第3次main 执行第4Thread-0 执行第4Thread-0 执行第5Thread-0 执行第6Thread-0 执行第7Thread-0 执行第8Thread-0 执行第9次main 执行第5次main 执行第6次main 执行第7次main 执行第8次main 执行第9

线程常用的方法:
Thread.yield():当前线程让出CPU,回到等待队列中,和其他线程一起再抢CPU,有可能再抢到也可能抢不到Thread.join(): 如果再t1线程中调用了t2.join(),表示调用后到t2线程运行,t2运行完了再回到t1运行,常用来线程等待,线程按序执行

线程的状态
创建状态:线程创建成功的状态就是创建状态
就绪状态:创建状态的线程启动时的状态就是就绪状态、运行状态的线程释放CPU资源就到了 就绪状态或阻塞状态的线程阻塞解除就到了就绪状态。
运行状态:就绪状态的线程获取CPU资源就到了运行状态。
阻塞状态:运行的线程等待用户输入、线程休眠等就到了线程阻塞状态或者
死亡状态:线程自然执行完毕或外部干涉终止线程,线程就到了死亡状态

如何获取线程的状态:thread.getState()。