day20_线程

复习

线程和进程的关系

  • 线程是资源调度的最小单位进程是资源分配的最小单位
  • 进程中包含线程
  • 资源分配给进程后,进程内多个线程共享进程资源

run()和start()区别?

  • run()是线程执行的方法

  • start()会开启新的线程,开启后会自动调用run执行线程任务

一、线程安全

什么是线程不安全?为什么不安全

  • 不安全是指,多个线程在同时执行时操作了同一个资源导致每个线程中结果与预想的不一致
  • 不安全是指,线程执行时没有达到原子操作,即执行过程中会被打断

线程安全:就是指在线程执行过程中,线程不被别的线程打断,数据不被破坏

如何实现线程安全:加锁

1.2窗口卖票

线程安全就是加锁!同步锁

  • 同步代码块
  • 同步方法

二、synchornized

synchronized同步锁,用法有两种

  • 同步方法,在方法上加上synchronized即可
  • 同步代码块,使用synchronized(锁){内容}

同步锁就可以实现被包裹的代码线程安全,即执行过程是原子操作不会被打断

2.1同步方法

需求: 开启两个线程,都无限执行.一个线程打印数字,一个线程打印英文,保证在打印过程中不被打断

解决方案: 给方法上锁

  • 但是要注意同步方法上锁成功的前提:多个线程共享一把锁!!
  • 同步方法默认锁对象是当前,即this
  • 所以如果多创建对象

2.2同步代码块

同步代码块,就是在方法内部使用synchronized(锁)+{内容},包裹部分代码实现上锁,但是要注意多个线程之间的锁要是同一个

2.3总结

使用上的区别:

  1. 代码想要保证安全,就加锁(synchronized)
  2. 如果是使用同步方法,锁的是this,想要锁保证this是同一个即可
  3. 如果是使用同步代码块,保证锁的任意对象是同一个即可

二者区别:

  • 同步方法锁住整个方法,锁的范围较大
  • 同步代码块锁的部分代码,锁住范围较小

2.4sleep

抱着锁睡觉

  • 没有加锁时候,sleep时线程会让出资源,别的线程执行
  • 加锁时,sleep时线程会抱着锁睡,不让出资源,别的线程会陷入阻塞状态

三、线程安全的类

StringBuffer 线程安全

StringBuilder 线程不安全

ArrayList 线程不安全

Vector 线程安全

HashMap 线程不安全

Hashtable 线程安全

ConcurrentHashMap 线程安全,效率快

  • 采用同步方法,锁了部分重要代码,没有锁全部代码 (分段锁机制)

四、死锁

死锁是指在多任务系统中,各个任务由于竞争资源而造成的一种互相等待的现象,导致任务无法继续执行的情况。死锁通常发生在多个任务同时需要多个共享资源,但是这些资源又只能被一个任务独占,但是另外一个任务不释放时!

死锁通常发生在四个必要条件同时满足时:

  1. 互斥条件:资源只能被一个任务占用,其他任务需要等待释放。
  2. 占有且等待:任务至少持有一个资源,并且在等待获取其他资源。
  3. 不可抢占:已经分配给一个任务的资源不能被其他任务抢占,只能由持有资源的任务主动释放。
  4. 循环等待:一系列任务互相持有其他任务所需要的资源,形成一个循环等待的关系。

为避免和解决死锁,可以采取以下策略:

  1. 避免死锁:通过破坏死锁的四个必要条件之一,来避免死锁的发生。比如,一次性获取所有需要的资源,或者按照一定的顺序获取资源。
  2. 检测和恢复:定期检测系统中是否存在死锁,一旦检测到死锁,采取恢复策略,比如中断一些任务,释放资源。
  3. 避免循环等待:为资源分配一个全局唯一的编号,任务按编号递增的顺序申请资源,释放资源则按相反的顺序进行,避免循环等待。
  4. 资源剥夺:当一个任务请求资源时,如果无法获取,可以暂时剥夺该任务已经持有的资源,让其他任务能够继续执行。
  5. 谨慎设计:在程序设计时,尽量避免使用多个资源互斥且不可抢占的情况,或者采取一些策略确保资源的合理分配和释放,减少死锁的发生可能性。

五、线程通信

5.1 介绍

通信: 线程之间可以相互通知

有了线程通信,可以做到让正在执行的线程暂停,通知另外一个执行;它执行完后暂停,通知我继续执行,即做到线程的交互执行, 而不是像以前一样出现成片的一直执行

如何实现线程通信,用的方法

  • wait() 让线程暂停处于等待状态
  • notify() 唤醒线程起来执行

但是这两个方法在Object类中!!! 为什么??

  • 因为线程通信的需要由当前锁对象来调用!!!
  • 锁对象可以是任意对象

前提: 要想实现线程通信,先要保证线程安全

提示: 如果是3个及以上线程的通信,需要使用wait+notifyAll()

5.3 sleep和wait区别[面试]

  • sleep是Thread类的静态方法,wait是Object类的方法

  • sleep让线程等待不执行,在加锁时,不让出资源 ;

    wait会让出资源

  • sleep()可以用在非同步代码中;wait()必须放在同步代码中且被锁对象调用

六、线程池

6.1 线程池概念

  • 如果有非常多的任务需要非常多的线程来完成,每个线程的工作时间不长,就需要创建很多线程,工作完又立即销毁[线程频繁创建和销毁线程]
  • 频繁创建和销毁线程非常消耗性能,那么线程池,就是可以创建一些线程,放在"池子"中,用的时候去池子取一个线程去使用,使用完再放回去,线程可以重用
  • 线程池,底层其实就是集合队列,里面存储线程对象,用的时候去抽即可,就不要频繁创建线程了

使用线程池的好处是

  • 减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。
  • 如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存(OOM Out Of Memory)或者“过度切换”的问题
  • –> 以上摘自阿里官方手册

6.2 线程池原理

在这里插入图片描述

将**任务(task)提交(submit/execute)给线程池(threadpool),**由线程池分配线程,运行任务,任务结束后,线程重新放入线程池供后续线程使用

6.3 创建线程池的方式

使用线程池创建线程,执行任务

JDK提供了关于创建线程池的方式

  • Executors: 通过该类提供的静态方法来获得不同特点的线程池对象
    • newFixedThreadPool
    • newCachedThreadPool
    • newScheduledThreadPool
    • newSingleThreadExecutor
  • ThreadPoolExecutor: 通过submit(Runnable task) 来提交任务,执行任务

线程池执行任务时,可以采用两种方法:

execute(): 没有返回值,无法判断任务是否执行成功

submit():会返回Future对象,通过该对象判断任务是否执行成功

线程池使用完要关闭时:

shutdown() 关闭线程池

6.4 不同特点的线程池

通过Executors调用以下静态方法获得不同特点的线程池对象

方法类型解释
newFixedThreadPool固定大小线程池池中包含固定数目的线程,空闲线程一直保留。只有核心线程,线程数量固定,任务队列为LinkedBlockingQueue
newCachedThreadPool动态大小的线程池,原则上无上限无核心线程,非核心线程数量无限,执行完闲置60s后回收,任务队列SynchronousQueue
newScheduledThreadPool可以执行定时任务的线程池用于调度执行的固定线程池,执行定时或周期性任务。和弦线程数量固定,非核心线程数量无线,执行完闲置10ms后回收,任务队列为DelayedWorkQueue
newSingleThreadExecutor线程线程池只有一个线程的池,会顺序执行提交的任务,只有一个核心线程,无非核心线程,任务队列为LinkdBlockingQueue
newSingleThread
ScheduledExecutor
单线程定时任务线程池
newWorkStealingPool1.8提供新的方式创建线程池

以上线程池操作在阿里java开发手册中是不建议用的…

说明:Executors 返回的线程池对象的弊端如下:
1)FixedThreadPool 和 SingleThreadPool:
允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
2)CachedThreadPool 和 ScheduledThreadPool:
允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
-----------------------
OOM 内存溢出,即系统资源耗尽

6.5 ThreadPoolExecutor[重要]

  • ThreadPoolExecutor 很重要,有7个参数
参数名解释备注
int corePoolSize线程池的线程数量(核心线程数)不能小于0
int maximumPoolSize线程池可支持的最大线程数最大数量>=核心线程数
long keepAliveTime指定临时线程的最大存活时间不能小于0
TimeUnit unit指定存活时间的单位(秒,分,时,天)时间单位
BlockingQueue workQueue指定任务队列
ThreadFactory threadFactory指定哪个线程工厂创建线程
RejectedExecutionHandler handler指定线程忙,任务队列满的时候新任务来了怎么办?拒绝策略

举例子: 海底捞吃饭

  1. 核心线程数: 核心服务人员3个
  2. 最大线程数: 允许最多的服务人员数量10, (其中7个临时找的)
  3. 最大存活时间: 临时工不干活时间
  4. 时间单位:
  5. 阻塞队列: 门口的排队的人
  6. 线程工厂: 如何将服务人员(线程)创建来的
  7. 拒绝策略: 再来的任务不再接收直接拒绝(发券下次来,本次不接客…)
    么办?拒绝策略 | |

举例子: 海底捞吃饭

  1. 核心线程数: 核心服务人员3个
  2. 最大线程数: 允许最多的服务人员数量10, (其中7个临时找的)
  3. 最大存活时间: 临时工不干活时间
  4. 时间单位:
  5. 阻塞队列: 门口的排队的人
  6. 线程工厂: 如何将服务人员(线程)创建来的
  7. 拒绝策略: 再来的任务不再接收直接拒绝(发券下次来,本次不接客…)

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值