浅谈JAVA多线程基本概念和创建使用

举报
运气男孩 发表于 2022/01/16 23:56:22 2022/01/16
【摘要】 多线程-基本概念 在说Java的多线程之前,首先来讲讲程序、进程、线程三者的定义:程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。 进程(process)是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。——生命周期  如:运行中的QQ,运行中的MP3播放器 程序是静态的,进程是动态的  进...

多线程-基本概念 

在说Java的多线程之前,首先来讲讲程序、进程、线程三者的定义:

程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。 

进程(process)是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。——生命周期  如:运行中的QQ,运行中的MP3播放器 程序是静态的,进程是动态的  进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域

线程(thread)  进程可进一步细化为线程,是一个程序内部的一条执行路。 若一个进程同一时间并行执行多个线程,就是支持多线程的  线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小 。一个进程中的多个线程共享相同的内存单元/内存地址空间—>它们从同一堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资源可能就会带来安全的隐患。 

举个例子看看,下面的就是windows系统后台的一下进程和我自己运行的应用软件

可以说进程里面包含了线程,如下图

接着来说说我对单核CPU和多核CPU的理解 

单核CPU,其实是一种假的多线程,因为在一个时间单元内,也只能执行一个线程的任务。  如果是多核的话,才能更好的发挥多线程的效率。(现在的服务器都是多核的)  一个Java应用程序java.exe,其实至少有三个线程:main()主线程,gc() 垃圾回收线程,异常处理线程。当然如果发生异常,会影响主线程。 

这里又必须提到操作系统的并行与并发 的概念了:

并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事。

并发:一个CPU(采用时间片)同时执行多个任务。比如:秒杀、多个人做同一件事。 

使用多线程的优点 

背景:以单核CPU为例,只使用单个线程先后完成多个任务(调用多个方法),肯定比用多个线程来完成用的时间更短,为何仍需多线程呢?

多线程程序的优点: 

  • 提高应用程序的响应。对图形化界面更有意义,可增强用户体验。  
  • 提高计算机系统CPU的利用率   改善程序结构。
  • 将既长又复杂的进程分为多个线程,独立运行,利于理解和修改  

那么何时需要多线程  ?

  • 程序需要同时执行两个或多个任务。  
  • 程序需要实现一些需要等待的任务时,如用户输入、文件读写 操作、网络操作、搜索等。  
  • 需要一些后台运行的程序时。 

线程的创建和启动 

Java语言的JVM允许程序运行多个线程,它通过java.lang.Thread 类来体现。  

Thread类的特性

  • 每个线程都是通过某个特定Thread对象的run()方法来完成操作的,经常 把run()方法的主体称为线程体 。
  • 通过该Thread对象的start()方法来启动这个线程,而非直接调用run()。 

创建新执行线程有3种方法:  

  • 继承Thread类的方式 (重点)  
  • 实现Runnable接口的方式(重点)
  • 实现Callable接口(JDK1.5之后)(了解) 

这里我只简单说一下前两种的创建方法,实现Callable接口大家自行了解一下。

方式一:继承Thread类  定义一个类继承Thread类。 子类中重写Thread类中的run方法。 创建Thread子类对象,即创建了线程对象。 调用线程对象start方法:启动线程,调用run方法。

这里我提出两个小问题:

为什么要重写run()方法?

因为run()是用来封装被线程执行的代码

run()方法和start()方法的区别?

run():封装线程执行的代码,直接调用,相当于普通方法的调用

start():启动线程;然后由JVM调用此线程的run()方法 

使用Thread类创建线程注意事项: 

  1. 如果自己手动调用run()方法,那么就只是普通方法,没有启动多线程。
  2. run()方法由JVM调用,什么时候调用,执行的过程控制都有操作系统的CPU 调度决定。 
  3. 想要启动多线程,必须调用start方法。
  4. 一个线程对象只能调用一次start()方法启动,如果重复调用了,则将抛出以上的异常“IllegalThreadStateException”。  

方式二:实现Runnable接口 

  1. 定义类,实现Runnable接口。 
  2. 子类中重写Runnable接口中的run方法。
  3. 通过Thread类含参构造器创建线程对象。 
  4. 将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。
  5. 调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法。 

方式三:实现Callable接口(是一个泛型接口)  

  1. 定义类,实现Callable接口,需要返回值类型。
  2. 类中重写call方法,需要抛出异常。
  3. 创建FutureTask对象,并将Callable对象传入FutureTask的构造方法中。
  4. 实例化Thread对象,并在构造方法中传入FurureTask对象。
  5. 调用Thread类的start方法:开启线程。 

与使用Runnable相比, Callable功能更强大些 

  • 相比run()方法,可以有返回值 
  • 方法可以抛出异常 
  • 支持泛型的返回值 
  • 需要借助FutureTask类,比如获取返回结果 

Future接口 

  • 可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。 
  • FutrueTask是Futrue接口的唯一的实现类 
  • FutureTask 同时实现了Runnable, Future接口。它既可以作为 Runnable被线程执行,又可以作为Future得到Callable的返回值 

最后,来做一个关于多线程的小练习吧

练 习:  创建两个分线程,让其中一个线程输出1-100之间的偶数,另一个线程输出1-100之间的奇数。  

最后输出的结果是,奇数和偶数交叉输出。

好了,关于此次JAVA多线程基本概念和创建使用就说到这里了,如有不足之处,欢迎指正!

感恩能与大家在华为云遇见!希望能与大家一起在华为云社区共同成长。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。