java多线程知识点总结

本文详细介绍了Java多线程的相关概念,包括进程、线程的区别,线程组、线程状态、线程同步与死锁,以及多线程的目的和使用场景。通过实例解释了线程的创建和执行过程,强调了线程池的重要性和使用线程池的原因。文中还探讨了多线程的安全问题,并提供了任务和线程池的定义以及线程池中线程的生命周期和任务仓库的概念。
摘要由CSDN通过智能技术生成

java多线程知识点总结


什么是进程?


 一个程序运行起来时在内存中开辟一段空间用来运行程序,这段空间包括heap、stack、data segment和code segment。例如,开一个QQ就表明开了一个QQ进程。

什么是线程?


 每一个进程中都至少有一个线程。线程是指程序中代码运行时的运行路径,一个线程表示一条路径。例如QQ进程中,发送消息、接收消息、接收文件、发送文件等各种独立的功能都需要一个线程来执行。

 世间万物都可以同时完成很多工作。例如,人体可以同时进行呼吸、血液循环、思考问题等活动。用户既可以使用计算机听歌,也可以编写文档和发送邮件,而这些活动的完成可以同时进行。这种同时执行多个操作的“思想”在 Java 中被称为并发,而将并发完成的每一件事称为线程。

 在 Java 中,并发机制非常重要,但并不是所有程序语言都支持线程。在以往的程序中,多以一个任务完成以后再进行下一个任务的模式进行,这样下一个任务的开始必须等待前一个任务的结束。Java 语言提供了并发机制,允许开发人员在程序中执行多个线程,每个线程完成一个功能,并与其他线程并发执行。这种机制被称为多线程。
 多线程是非常复杂的机制,比如同时阅读 3 本书。首先阅读第 1 本第 1 章,然后再阅读第 2 本第 1 章,再阅读第 3 本第 1 章,接着回过头阅读第 1 本第 2 章,以此类推,就体现了多线程的复杂性。

 既然多线程这么复杂,那么它在操作系统中是怎样工作的呢?其实,Java 中的多线程在每个操作系统中的运行方式也存在差异,在此以 Windows 操作系统为例介绍其运行模式。

 Windows 系统是多任务操作系统,它以进程为单位。一个进程是一个包含有自身地址的程序,每个独立执行的程序都称为进程,也就是正在执行的程序。图 1 所示为 Windows 7 系统下使用任务管理器查看进程的结果。
在这里插入图片描述
 系统可以分配给每个进程一段有限的执行 CPU 的时间(也称为 CPU 时间片),CPU 在这段时间中执行某个进程,然后下一个时间段又跳到另一个进程中去执行。由于 CPU 切换的速度非常快,给使用者的感受就是这些任务似乎在同时运行,所以使用多线程技术后,可以在同一时间内运行更多不同种类的任务。

 图 2 的左图是单线程环境下任务 1 和任务 2 的执行模式。任务 1 和任务 2 是两个完全独立、互不相关的任务,任务 1 是在等待远程服务器返回数据,以便进行后期的处理,这时 CPU 一直处于等待状态,一直在“空运行”。如果任务 2 是在 5 秒之后被运行,虽然执行任务 2 用的时间非常短,仅仅是 1 秒,但必须在任务1运行结束后才可以运行任务 2。由于运行在单任务环境中,所以任务 2 有非常长的等待时间,系统运行效率大幅降低。

 单任务的特点就是排队执行,也就是同步,就像在 cmd 中输入一条命令后,必须等待这条命令执行完才可以执行下一条命令一样。这就是单任务环境的缺点,即 CPU 利用率大幅降低。

图2 单线程和多线程执行模式

 右侧则是多线程环境下的执行模式。从中可以发现,CPU 完全可以在任务 1 和任务 2 之间来回切换,使任务 2 不必等到 5 秒再运行,系统的运行效率大大得到提升。这就是要使用多线程技术、要学习多线程的原因。

 那么什么是线程呢?线程可以理解成是在进程中独立运行的子任务。比如,QQ.exe 运行时就有很多的子任务在同时运行。像好友视频、下载文件、传输数据、发送表情等,这些不同的任务或者说功能都可以同时运行,其中每一项任务完全可以理解成是“线程”在工作,传文件、听音乐、发送图片表情等功能都有对应的线程在后台默默地运行。

以上知识点摘抄自 Java线程的概念:什么是线程?
http://c.biancheng.net/view/1157.html


进程和线程的区别:


 从资源的角度来考虑,进程主要考虑的是CPU和内存,而线程主要考虑的是CPU的调度,某进程中的各线程之间可以共享这个进程的很多资源。
 从粒度粗细来考虑,进程的粒度较粗,进程上下文切换时消耗的CPU资源较多。线程的粒度要小的多,虽然线程也会切换,但因为共享进程的上下文,相比进程上下文切换而言,同进程内的线程切换时消耗的资源要小的多的多。在JAVA中,除了java运行时启动的JVM是一个进程,其他所有任务都以线程的方式执行,也就是说java应用程序是单进程的,甚至可以说没有进程的概念。

什么是线程组(ThreadGroup)


 线程组提供了一些批量管理线程的方法,因此通过将线程加入到线程组中,可以更方便地管理这些线程。

线程的状态


 就绪态、运行态、睡眠态。还可以分为存活和死亡,死亡表示线程结束,非死亡则存活,因此存活包含就绪、运行、睡眠。

线程中断睡眠(interrupt)


将线程从睡眠态强制唤醒,唤醒后线程将进入就绪队列等待cpu调度。

并发操作


多个线程同时操作一个资源。这会带来多线程安全问题,解决方法是使用线程同步。

线程同步


 让线程中的某些任务原子化,即要么全部执行完毕,要么不开始执行。通过互斥锁来实现同步,通过监视这个互斥锁是否被谁持有来决定是否从睡眠态转为就绪态(即从线程池中出去),也就是是否有资格去获取cpu的执行权。线程同步解决了线程安全的问题,但降低了程序的效率。

死锁


 线程全睡眠了无法被唤醒,导致程序卡死在某一处无法再执行下去。典型的是两个同步线程,线程1持有A锁,且等待B锁,但线程2持有B锁且等待A锁,这样的僵局会造成死锁。但需要注意的是,死锁并非都是因为僵局,只要两边的线程都无法继续向下执行代码(或者两边的线程池都无法被唤醒,这是等价的概念,因为锁等待也会让进程进入睡眠态),则都是死锁。

用多线程的目的是什么?


 以前我认为多线程的作用就是提升性能。实际上,多线程并不一定能提升性能(甚至还会降低性能);多线程也不只是为了提升性能。多线程主要有以下的应用场景:

  1. 避免阻塞(异步调用)

 单个线程中的程序,是顺序执行的。如果前面的操作发生了阻塞,那么就会影响到后面的操作。这时候可以采用多线程,我感觉就等于是异步调用。这样的例子有很多:
 ajax调用,就是浏览器会启一个新的线程,不阻塞当前页面的正常操作;
 流程在某个环节调用web service,如果是同步调用,则需要等待web service调用结果,可以启动新线程来调用,不影响主流程;
 android里,不要在ui thread里执行耗时操作,否则容易引发ANR;
 创建工单时,需要级联往其他表中插入数据,可以将级联插入的动作放到新线程中,先返回工单创建的结果……

  1. 避免CPU空转

 以http server为例,如果只用单线程响应HTTP请求,即处理完一条请求,再处理下一条请求的话,CPU会存在大量的闲置时间
 因为处理一条请求,经常涉及到RPC、数据库访问、磁盘IO等操作,这些操作的速度比CPU慢很多,而在等待这些响应的时候,CPU却不能去处理新的请求,因此http server的性能就很差
 所以很多web容器,都采用对每个请求创建新线程来响应的方式实现,这样在等待请求A的IO操作的等待时间里,就可以去继续处理请求B,对并发的响应性就好了很多
 当然,这种设计方式并不是绝对的,现在像node.js、Nginx等新一代http server,采用了事件驱动的实现方式,用单线程来响应多个请求也是没问题的。甚至实现了更高的性能,因为多线程是一把双刃剑,在提升了响应性的同时,创建销毁线程都是需要开销的,另外CPU在线程之间切换,也会带来额外的开销。避免了这些额外开销,可能是node.js等http server性能优秀的原因之一吧

  1. 提升性能

 在满足条件的前提下,多线程确实能提升性能
 打一个比方,多线程就相当于,把要炒的菜放到了不同的锅里,然后用不同的炉来炒,当然速度会比较快。本来需要先炒西红柿,10分钟;再炒白菜10分钟;加起来就需要20分钟。用了多线程以后,分别放在2个锅里炒,10分钟就都炒好了

基本上,需要满足3个条件:

第1,任务具有并发性,也就是可以拆分成多个子任务。并不是什么任务都能拆分的,条件还比较苛刻

子任务之间不能有先后顺序的依赖,必须是允许并行的

比如

Java代码

a = b + c
d = e + f

这个是可以并行的;

Java代码

a = b + c
d = a + e

这个就无法并行了,第2步计算需要依赖第1步的计算结果,即使分成2个线程,也不会带来任何性能提升

另外,还不能有资源竞争。比如2个线程都需要写一个文件,第1个线程将文件锁定了,第2个线程只能等着,这样的2个子任务,也不具备并发性;执行sychronized代码,也是同样的情况

第2,只有在CPU是性能瓶颈的情况下,多线程才能实现提升性能的目的。比如一段程序,瓶颈在于IO操作,那么把这个程序拆分到2个线程中执行,也是无法提升性能的

第3,有点像废话,就是需要有多核CPU才行。否则的话,虽然拆分成了多个可并行的子任务,但是没有足够的CPU,还是只有一个CPU在多个线程中切换来切换去,不但达不到提升性能的效果,反而由于增加了额外的开销,而降低了性能。类似于虽然把菜放到了2个锅里,但是只有1个炉子一样

如果上述条件都满足,有一个经验公式可以计算性能提升的比例,叫阿姆达尔定律:

速度提升比例 = 1/[(1-P)+(P/N)],其中P是可并行任务的比例,N是CPU核心数量

假设CPU核心是无限的,则公式简化为1/(1-P)

假设P达到了80%(已经非常高了),那么速度提升比例也只能达到5倍而已

总结:
 多线程可以使程序反应更快,交互性更强,执行效率更高。即使在单处理器系统上,多线程程序的运行速度也比单线程程序更快。java对多线程程序的创建及运行,以及锁定资源以避免冲突提供了非常好的支持。
 可以在程序中创建附加的线程以执行并发任务。在java中,每个任务都是Runnable
接口的一个实例,也称为可运行对象.线程本质上讲就是便于任务执行的对象。

原文链接:https://blog.csdn.net/ajax_yan/article/details/82149362
————————————————
版权声明:本文为CSDN博主「励志重写JDK」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。


什么是任务?


 任务就是对象。为了创建任务,必须首先为任务定义一个实现Runnable接口的类。Runnable接口非常简单。他只包含一个run()方法。需要实现这个方法来告诉系统线程将如何运行。任务必须在线程中执行。从java代码层面看任务就是Runnable对象的run()方法内的代码块。在java程序中任务的执行包括第三个步骤:

  1. 创建一个任务类,该类实现Runnable接口
  2. 使用任务类创建一个线程
  3. 调用线程的start()方法,告诉java虚拟机该线程准备运行

开发一个任务类的模板如下如代码所示

package com.poolStudy;

public class TaskThreadFemo {
   

    public static void main(String[] args) {
   
        Runnable printA = new PrintChar('a',100)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值