深入浅出:Java多线程编程实战(一)

前言

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

深入浅出:Java多线程编程实战(一)_第1张图片

一、进程与线程

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

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

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

深入浅出:Java多线程编程实战(一)_第2张图片

二、同步与异步

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


三、并行与并发

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

深入浅出:Java多线程编程实战(一)_第3张图片

并发任务执行过程:

深入浅出:Java多线程编程实战(一)_第4张图片

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


四、线程的状态

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

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。

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

深入浅出:Java多线程编程实战(一)_第5张图片


五、创建线程的三种方式

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

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

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

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

深入浅出:Java多线程编程实战(一)_第6张图片

Java程序运行时默认的主线程,main()方法的方法体就是主线程的线程执行体。

可以看到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()方法来启动该线程

深入浅出:Java多线程编程实战(一)_第7张图片

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


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

通过实现Runnable接口创建多线程时,Thread类的作用就是把run()方法包装成线程执行体.从Java5开始,Java提供了Callable接口,该接口可以理解为是Runnable接口的增强版,Callable接口提供了一个call()方法可以作为线程执行体,但call()方法比run()方法功能更强大,call()方法可以有返回值.call()方法可以声明抛出的异常。

但是Callable接口并不是Runnable接口的子接口,所以Callable对象不能直接作为Thread的target.而且call()方法还有一个返回值,call()方法并不是直接调用的,它是作为线程执行体被调用的.好在Java提供了Future接口来代表Callable接口里的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()方法来获得子线程执行结束后的返回值.

深入浅出:Java多线程编程实战(一)_第8张图片

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

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

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

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

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

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

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

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

    之后还会继续跟大家分享多线程的三大核心、线程池及多线程的三大核心等内容,要学习请继续关注,若文中有所错误之处,还望提出!学习是一个循序渐进的过程,需要时间精力,更需要技巧和规律。天赋往往存在少数人身上,绝大多数人只能不断摸索,有的人花费大量时间还一无所获,有些人却既能轻松工作,又享受了生活。

    其中的原因不得而知:站在巨人的肩膀上必定是要比站在地上攀爬的人快一步的。

    为了让学习变得轻松高效, 现在给大家提供一个学习平台,让你在实践中积累经验掌握原理。主要方向是JAVA架构师,在这里你可以学习Java工程化、高性能及分布式、深入浅出、性能调优、Spring,MyBatis,Netty源码分析和大数据等知识点。可以加入Java后端技术群:819940388群里有阿里大牛直播讲解技术,或是关注微信公众号:Java资讯库,回复“架构”,免费的大型互联网Java技术视频分享给大家。

深入浅出:Java多线程编程实战(一)_第9张图片

你可能感兴趣的:(深入浅出:Java多线程编程实战(一))