多线程、同步异步及阻塞和非阻塞

多线程、同步异步及阻塞和非阻塞

进程和线程的概念

  • 进程:运行中的应用程序称为进程,拥有系统资源(cpu、内存)

  • 线程:进程中的一段代码,一个进程中可以有多段代码。本身不拥有资源(共享所在进程的资源);

在java中,程序入口被自动创建为主线程,在主线程中可以创建多个子线程。

区别:

  1. 是否占有资源问题
  2. 创建或撤销一个进程所需要的开销比创建或撤销一个线程所需要的开销大。
  3. 进程为重量级组件,线程为轻量级组件

    • 多进程: 在操作系统中能同时运行多个任务(程序)

    • 多线程: 在同一应用程序中有多个功能流同时执行

已经有了进程,为什么还会需要线程呢?主要原因如下:

许多应用程序中,同时发生着多个活动。将这些应用程序分解成多个准并行的线程,程序设计的模型会变成更加简单。 
由于线程比进程进行更加轻量,创建和取消更加容易。 
如果程序是IO密集型,那么多线程执行能够加快程序的执行速度。(如果是CPU密集型,则没有这个优势) 
在多CPU系统中,多线程是可以真正并行执行的。

线程的主要特点

  1. 不能以一个文件名的方式独立存在在磁盘中;

  2. 不能单独执行,只有在进程启动后才可启动;

  3. 线程可以共享进程相同的内存(代码与数据)。

多线程原理

同一时间,CPU只能处理1条线程,只有1条线程在工作(执行) 
多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换) 
如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象

思考:如果线程非常非常多,会发生什么情况?

CPU会在N多线程之间调度,CPU会累死,消耗大量的CPU资源

每条线程被调度执行的频次会降低(线程的执行效率降低)

线程的主要用途

  1. 利用它可以完成重复性的工作(如实现动画、声音等的播放)。

  2. 从事一次性较费时的初始化工作(如网络连接、声音数据文件的加载)。

  3. 并发执行的运行效果(一个进程多个线程)以实现更复杂的功能

多线程(多个线程同时运行)程序的优缺点

优点:

  1. 可以减轻系统性能方面的瓶颈,因为可以并行操作;

  2. 提高CPU的处理器的效率,在多线程中,通过优先级管理,可以使重要的程序优先操作,提高了任务管理的灵活性;

另一方面,在多CPU系统中,可以把不同的线程在不同的CPU中执行,真正做到同时处理多任务。

缺点:

  1. 开启线程需要占用一定的内存空间(默认情况下,主线程占用1M,子线程占用512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能

  2. 线程越多,CPU在调度线程上的开销就越大

  3. 程序设计更加复杂:比如线程之间的通信、多线程的数据共享

多线程的生命周期

线程状态

与人有生老病死一样,线程也同样要经历新建、就绪、运行(活动)、阻塞和死亡五种不同的状态。这五种状态都可以通过Thread类中的方法进行控制。

创建并运行线程:

  1. 新建状态(New Thread):在Java语言中使用new 操作符创建一个线程后,该线程仅仅是一个空对象,它具备类线程的一些特征,但此时系统没有为其分配资源,这时的线程处于创建状态。

    1. 线程处于创建状态时,可通过Thread类的方法来设置各种属性,如线程的优先级(setPriority)、线程名(setName)和线程的类型(setDaemon)等。
  2. 就绪状态(Runnable):使用start()方法启动一个线程后,系统为该线程分配了除CPU外的所需资源,使该线程处于就绪状态。此外,如果某个线程执行了yield()方法,那么该线程会被暂时剥夺CPU资源,重新进入就绪状态。

  3. 运行状态(Running):Java运行系统通过调度选中一个处于就绪状态的线程,使其占有CPU并转为运行状态。此时,系统真正执行线程的run()方法。

    1. 可以通过Thread类的isAlive方法来判断线程是否处于就绪/运行状态:当线程处于就绪/运行状态时,isAlive返回true,当isAlive返回false时,可能线程处于阻塞状态,也可能处于停止状态。
  4. 阻塞和唤醒线程

    1. 阻塞状态(Blocked):一个正在运行的线程因某些原因不能继续运行时,就进入阻塞 状态。这些原因包括:
    2. 等待阻塞:当线程执行了某个对象的wait()方法时,线程会被置入该对象的等待集中,直到执行了该对象的notify()方法wait()/notify()方法的执行要求线程首先获得该对象的锁。
    3. 同步阻塞:当多个线程试图进入某个同步区域(同步锁)时,没能进入该同步区域(同步锁)的线程会被置入锁定集(锁池)中,直到获得该同步区域的锁,进入就绪状态。
    4. 其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

5.死亡状态(Dead):线程在run()方法执行结束后进入死亡状态。此外,如果线程执行了interrupt()或stop()方法,那么它也会以异常退出的方式进入死亡状态。

终止线程的三种方法

① 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止,推荐使用。

② 使用stop方法强制终止线程(这个方法不推荐使用,因为stop和suppend、resume一样,也可能发生不可预料的结果)。

③ 使用interrupt方法中断线程。

同步/异步,阻塞/非阻塞概念解释

同步/异步, 它们是消息的通知机制.

阻塞/非阻塞, 它们是程序在等待消息(无所谓同步或者异步)时的状态.

同步

所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。 
简单来说就是当前程序执行完才能执行后面的程序,程序执行时按照顺序执行,平时写的代码基本都是同步的;

异步

异步的概念和同步相对。 
当一个异步过程调用发出后,调用者不会立刻得到结果。实际处理这个调用的部件是在调用发出后,通过状态、通知来通知调用者,或通过回调函数处理这个调用。 
简单来说就是程序没有等到上一步程序执行完才执行下一步,而是直接往下执行,前提是下面的程序没有用到异步操作的值,异步的实现方式基本上都是多线程(定时任务也可实现,但是情况少)。

阻塞

阻塞调用是指调用结果返回之前,当前线程会被挂起。函数只有在得到结果之后才会返回。 
有人也许会把阻塞调用和同步调用等同起来,实际上他是不同的。 
对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回而已。

非阻塞

非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。

简单示例

老张烧水 
老张爱喝茶,废话不说,煮开水。 
出场人物:老张,水壶两把(普通水壶,简称水壶;会响的水壶,简称响水壶)。 
1 老张把水壶放到火上,立等水开。(同步阻塞)

老张觉得自己有点傻 
2 老张把水壶放到火上,去客厅看电视,时不时去厨房看看水开没有。(同步非阻塞)

老张还是觉得自己有点傻,于是变高端了,买了把会响笛的那种水壶。水开之后,能大声发出嘀~~~~的噪音。 
3 老张把响水壶放到火上,立等水开。(异步阻塞)(本可以坐着等通知的却非要立即等着,实际不大会出现这种情况,异步异步阻塞没有实际意义)

老张觉得这样傻等意义不大 
4 老张把响水壶放到火上,去客厅看电视,水壶响之前不再去看它了,响了再去拿壶。(异步非阻塞)

所谓同步异步,只是对于水壶而言。

普通水壶,同步;响水壶,异步。 
虽然都能干活,但响水壶可以在自己完工之后,提示老张水开了。这是普通水壶所不能及的。 
同步只能让调用者去轮询自己(情况2中),造成老张效率的低下。

所谓阻塞非阻塞,仅仅对于老张而言。 
立等的老张,阻塞;看电视的老张,非阻塞。

情况1和情况3中老张就是阻塞的,媳妇喊他都不知道。虽然3中响水壶是异步的,可对于立等的老张没有太大的意义。所以一般异步是配合非阻塞使用的,这样才能发挥异步的效用。

同步阻塞关系

线程阻塞,除了程序主动调用休眠外常见的就是程序遇到同步代码块,同一时间不能并行执行,当有多个请求了出现线程等待的情况即为阻塞。

同步原因

阻塞源于同步代码块,首先需要弄清楚何时需要同步,需要同步的地方是因为多个线程操作了同一个变量,导致在并行执行时变量值的混乱,故需要加同步锁来实现同一时间只能有同一个线程执行同步代码块中的程序,如果不涉及多线程操作同一个变量的情况是不需要使用同步的,在多线程编程时尽量避免操作公共变量来避免阻塞。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值