2020年学Java多线程,最好的教程素材_极悦注册
专注Java教育14年 全国咨询/投诉热线:444-1124-454
极悦LOGO图
始于2009,口口相传的Java黄埔军校
首页 学习攻略 Java学习 2020年学Java多线程,最好的教程素材

2020年学Java多线程,最好的教程素材

更新时间:2019-12-31 09:36:09 来源:极悦 浏览1902次


  在开发中我们经常使用线程来优化程序,提高系统执行效率,今天我们就来简单概述一下Java开发过程中需要了解的多线程知识点


2020年学Java多线程,最好的教程素材


  一、进程与线程


  进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。


  线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。线程是程序中一个单一的顺序控制流程,在单个程序中同时运行多个线程完成不同的工作,称为多线程。


  进程和线程的关系可以用下图来描述:


  二、同步与异步


  对于一次方法的调用来说,同步方法调用一旦开始,就必须等待该方法的调用返回,后续的方法才可以继续执行;异步的话,方法调用一旦开始,就可以立即返回,调用者可以执行后续的方法,这里的异步方法通常会在另一个线程里真实的执行,而不会妨碍当前线程的执行。


  三、并行与并发


  并发和并行是两个相对容易比较混淆的概念。他都可以表示在同一时间范围内有两个或多个任务同时在执行,但其在任务调度的时候还是有区别的,首先看下图:


  并发任务执行过程:


  从上图中可以看到,两个任务在执行的时候,并发是没有时间上的重叠的,两个任务是交替执行的,由于切换的非常快,对于外界调用者来说相当于同一时刻多个任务一起执行了;而并行可以看到时间上是由重叠的,也就是说并行才是真正意义上的同一时刻可以有多个任务同时执行。


  四、线程的状态


  线程从创建、运行到结束总是处于下面五个状态之一:新建状态、就绪状态、运行状态、阻塞状态及死亡状态。


  1、新建状态(New):


  当用new操作符创建一个线程时,例如new Thread(r),线程还没有开始运行,此时线程处在新建状态。 当一个线程处于新生状态时,程序还没有开始运行线程中的代码。


  2、就绪状态(Runnable)


  一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态,处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间,只有获得CPU时间才可以运行线程。因为在单CPU的计算机系统中,不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态。因此此时可能有多个线程处于就绪状态,对多个处于就绪状态的线程是由Java运行时系统的线程调度程序(thread scheduler)来调度的。


  3、运行状态(Running)


  当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法。


  4、 阻塞状态(Blocked)


  线程运行过程中,可能由于各种原因进入阻塞状态: 1)线程通过调用sleep方法进入睡眠状态; 2)线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者; 3)线程试图得到一个锁,而该锁正被其他线程持有; 4)线程在等待某个触发条件; ...... 所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。


  5、 死亡状态(Dead)


  有两个原因会导致线程死亡: 1) run方法正常退出而自然死亡; 2) 一个未捕获的异常终止了run方法而使线程猝死。 为了确定线程在当前是否存活(就是要么是可运行的,要么是被阻塞了),需要使用isAlive方法。如果是可运行或被阻塞,这个方法返回true; 如果线程仍旧是new状态且不是可运行的, 或者线程死亡了,则返回false。


  现在我们用一张图来说明它们之间的状态:


  五、创建线程的三种方式


  (1)继承Thread类创建线程类


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


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


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


  方法的方法体就是主线程的线程执行体。


  可以看到Thread-0和Thread-1两个线程的输出的i变量不连续 注意:i变量是FirstThread的实例变量,而不是局部变量,但是因为程序每次创建线程对象都需要创建一个FirstThread对象,所以Thread-0和Thread-1不能共享该实例变量。


  使用继承Thread类的方法来创建线程类时,多个线程之间是无法共享线程类的实例变量。


  (2) 实现Runnable接口创建线程类


  1、定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。


  2、创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。


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


  当线程类实现Runnable接口时,如果想获取当前线程,只能用Thread.currentThread()方法可以看到两个子线程的i变量是连续的这是因为采用Runnable接口的方式创建的多个线程可以共享线程类的实例变量.是因为:程序创建的Runnable对象只是线程的target,而多个线程可以共享一个target,所以多个线程可以共享一个线程类(实际上应该是线程的target类)的实例变量。


  (3)使用Callable和Future创建线程


  通过实现Runnable接口创建多线程时,Thread类的作用就是把run()方法包装成线程执行体.从方法可以声明抛出的异常。


  但是Callable接口并不是Runnable接口的子接口,所以Callable对象不能直接作为Thread的target.而且call()方法还有一个返回值,call()方法并不是直接调用的,它是作为线程执行体被调用的.好在方法的返回值,并为Future接口提供了一个FutureTask实现类,该实现类既实现了Future接口,并实现了Runnable接口----可以作为Thread类的target。


  在Future接口里定义了几个公共方法来控制它关联的Callable任务。


  Callable接口有泛型限制,并且Callable接口里的泛型形参类型与call()方法返回值类型相同.而且Callable接口是函数式接口,可以用Lambda表达式创建Callable对象。


  创建并启动具有返回值的线程的步骤如下:


  1、创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,且该call()方法有返回值,再创建Callable实现类的实例。


  2、使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。


  3、使用FutureTask对象作为Thread对象的target创建并启动新线程。


  4、调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。


  (4)创建线程的三种方式对比


  采用实现Runnable、Callable接口的方式创建多线程的优缺点:


  1、线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。


  2、多个线程可以共享同一个target对象,非常适合多个相同线程来处理同一份资源的情况,较好的体现了面向对象的思想。


  3、需要访问当前线程,则必须使用Thread.currentThread()方法。


  采用继承Thread类的方式创建多线程的优缺点:


  1、因为该线程已经继承了Thread类,所以不能在继承其他父类。


  2、编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。


2020年学Java多线程,最好的教程素材


       以上就是极悦注册机构小编介绍的“2020年学Java多线程,最好的教程素材”的内容,希望对大家有帮助,如有疑问,请在线咨询,有专业老师随时为你服务。


  相关内容


  


  


  


  


  


提交申请后,顾问老师会电话与您沟通安排学习

免费课程推荐 >>
技术文档推荐 >>