分享

Java基础之多线程没那么复杂

 昵称63557093 2020-03-03

  多线程的引入

  1.什么是多线程

  线程是程序执行的一条路径,一个进程中可以包含多条线程;多线程并发执行可以提高程序的效率

  2.进程和线程之间的关系

  操作系统可以同时执行多个任务,每个任务就是进程;进程可以同时执行多个任务,每个任务就是线程。

  3.多线程的应用场景

  红蜘蛛同时共享屏幕给多个电脑

  迅雷开启多条线程一起下载

  QQ开启多人聊天

  服务器同时处理多个客户的请求

  多线程并行和并发的区别

  并行性和并发性是两个概念,并行性指在同一时刻,有多条指令在多个处理器上同时执行

  并发性指的是同一时刻只有一条指令被执行,但多个进程指令被快速切换执行是的在宏观上具有多个进程被同时执行的效果

  Java程序运行原理和JVM的启动是多线程的吗?

  java程序运行原理

  Java命令启动jvm,启动jvm等于启动一个应用程序,也就是启动了一个进程,该进程会自动启动一个“主线程”,然后主线程去调用某个类的main方法

  Jvm启动是多线程的的吗

  JVM启动至少启动了垃圾回收线程和主线程,所以是多线程

  多线程的实现方式

  1. 继承Thread类创建线程类

  (1) 定义Thread的子类,并重写该类的run方法,该ru的执行体就代表了线程需要完成的任务,因此run()方法被称为线程执行体

  (2) 创建Thread子类的实例,即创建了线程对象

  (3) 调用线程对象的start()方法启动该线程

  上述代码的执行验证了多线程,如果上述程序的执行过程是多线程的话,会发现屏幕中的 ”我是主方法”和“我是run方法” 的字样是交替出现的,这说明了程序的的执行过程为并行执行Thread类的Start()方法启动run()方法的线程,和主方法中的执行同时进行。

  2.实现Runnable接口创建线程类

  (1) 定义一个实现了Runnable接口的实现类

  (2) 创建Runnable实现类的实例

  (3) 将创建的实例作为Thread类的target类创建Thread对象,该对象才是真正的线程对象 

  (4) 用创建的Thread对象启动线程

  查看API文档,会发现Runnable接口只定义了run()方法这一个抽象类,所以实现Runnable接口的实现类只有run()方法,仅作为线程执行体,所以,Runnable对象仅仅作为Thread对象的target,而实际的线程对象依然是Thread实例,Thread实例负责执行target的run()方法。

  3.实现Callable接口和Future接口创建多线程

  (1)Callable接口更像是Runnable接口的增强版,相比较Runable接口,Call()方法新增捕获和抛出异常的功能;Call()方法可以返回值

  (2)Future接口提供了一个实现类FutureTask实现类,FutureTaks类用来保存Call()方法的返回值,并作为Thread类的target。

  (3)调用FutureTask的get()方法来获取返回值

  上面的程序中,FutufeTask方法的get()方法将获得Call()方法的返回值,但是该方法将导致主线程受阻直到Call()方法结束并返回为止。

  4.三种实现多线程的方式对比

  通过上面的对比发现,一般在项目中,我们使用Runnable接或者Callable接口来实现多线程。

  5.线程的生命周期

  线程并不是创建过之后就开始执行的,也不是一直处于执行状态,一个线程的生命周期一个又五个:新建、就绪、运行、阻塞、死亡。
2.线程的这五种状态的原理参照《计算机组成原理2》多线程部分

  6.控制线程

  1.join线程

  1.Thread类中的成员方法,让调用join()方法的线程处于等待状态,直到子线程执行结束
2.join()方法通常由使用线程的程序调用,用于将一个线程拆分成若干小的线程执行,执行结束最后由主线程进行进一步的操作

  代码演示:

  mian线程调用join之后,出去阻塞状态,两个子线程并发执行。直到两个子程序执行结束之后,main线程才开始执行。

  join()线程有如下三种重载形式

  1.join() :

  2.join(long millis) :被join的线程的时间最长为millis毫秒,如果超过了这个millis则线程不被执行

  3.join(long millis,int nanos) :被join的线程的时间最长为millis毫秒+nanos微秒,如果超过了这个时间则线程不被执行 

  第三种重载行驶一般用不到,因为无论是计算机系统还是计算机硬件,还没发精确到微秒

  2.后台线程

  后台线程运行于后台,任务是为其他线程提供服务,也被称为“守护线程”或“精灵线程”。JVM的垃圾回收机制就是一个典型的后台线程。
通过调用Thread类的setDaemon(true)方法将线程设置为后台线程

  代码演示:

  运行结果:

image

  1.Thread-0的后台进程本该当i=999的时候才停止进行,但是程序中只进行到114次,这是因为所有的前台进程结束之后,后天进程的存在也就是去了意义,所以后台进程也跟着死亡。

  2.前台线程死亡之后,JVM会通知后台线程死亡,但是从后台线程接收指令到做出反应需要一定的时间,这就是为什么上述程序中的后台进程在main线程死亡之后后台进程还进行到114的原因。

  3.线程睡眠

  如果需要线程停顿一段时间进入阻塞状态,可以调用Thread类的静态方法sleep(),sleep()有两种重载形式

  sleep(long millis)

  让当前正在执行的线程暂停mili毫秒,进入阻塞状态。该方法受到系统计时器和线程调度器精度的影响。

  sleep(long millis,int nanos)

  让当前正在执行的线程暂停milis毫秒+nanos微秒,进入阻塞状态。该方法受到系统计时器和线程调度器精度的影响(不常用)

  代码演示:

  运行结果:

  观察程序想运行过程会发现,每一个进程之间相隔1秒。

  4.线程让步

  调用Thrad类的静态方法 yield()方法可以实现线程让步,和sleep()方法类似,yield()方法也是让当前正在运行的线程暂停,但是不会使线程阻塞,而是让线程进入就绪状态,让线程调度器重新安排一次线程调度,完全有可能出现的状况是,刚刚调用yield()方法进入就绪状态的线程就被线程调度器重新调度出来重新执行。

  代码演示:

  1.线程调用yield()方法后将执行的机会让给优先级相同的线程
2.高优先级的线程调用yield()方法暂停之后,系统中没有与之相同优先级和更高的优先级的线程,则线程调度器会将该线程重新调度出来,重新执行。

  sleep()方法和yield()方法比较

  5.改变线程优先级

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多