Java核心语法——多线程2(几种创建方式对比、多线程通信、互斥锁、信号量、等待机制)简述


在这里插入图片描述对上篇文章【Java核心语法——多线程】做个补充扩展,👉 https://blog.csdn.net/Today_He/article/details/108621582

一、几种多线程的创建方式

题外话:线程生命周期(新建、就绪、运行、阻塞和死亡)
官方承认的只有两种,Thread和Runnable,其他都是设计模式。

1.继承Thread类创建线程类

(1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。
(2)创建Thread子类的实例,即创建了线程对象。
(3)调用线程对象的start()方法来启动该线程。

2.通过Runnable接口创建线程类

(1)定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
(2)创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
(3)调用线程对象的start()方法来启动该线程。

3.通过Callable和Future创建线程

(1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
(2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。注释:FutureTask是一个包装器,它通过接受Callable来创建,它同时实现了Future和Runnable接口。
(3)使用FutureTask对象作为Thread对象的target创建并启动新线程。
(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

4.Excutor线程池

线程池提供了一个线程队列,队列中保存所有等待状态的线程,避免创建与销毁额外开销,提高了响应速度。

4.1.不推荐,Executors自动创建

1) FixedThreadPool和SingleThreadPool
允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM(内存不足OutOfMemoryError)
2) CachedThreadPool
允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM

4.2.推荐,手动创建ThreadPoolExcutor

不允许使用Executors自动创建,而是通过ThreadPoolExecutor的方式,这样处理可以让程序员更加明确线程池的运行规则,规避资源耗尽的风险。

举例:
在这里插入图片描述

4.2.1.核心线程数如何设置?
  • CPU密集型:CPU核数 + 1
  • IO密集型:
     等待时间多:(线程等待时间/CPU时间 + 1) * CPU核数
     等待时间少:CPU核数 * 2
  • 混合型:划分为CPU和IO类型,用不同的线程池

5.创建线程的三种方式的对比

5.1.采用实现Runnable、Callable接口的方式创见多线程时

优势是:
(1)线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。
(2)在这种方式下,多个线程可以共享同一个target(无特殊含义,就是Runnable、Callable的变量名,一般叫做target)对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
劣势是:
(1)编程稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread()方法。

5.2.使用继承Thread类的方式创建多线程时

优势是:
(1)编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。
劣势是:
(1) 线程类已经继承了Thread类,所以不能再继承其他父类。

二、多线程之间如何通信?

两种模式:对象共享、消息传递
 对象共享:volatile,多个线程监听同一个对象
 消息传递:Object的wait与notify;LockSupprot的park与unpark

注意:
 wait会释放锁;notify不会释放锁,当前逻辑执行完成才会唤醒
 wait最好放在while循环中,阻塞前后都会做条件判断
 unpark、park忽略顺序,都可以唤醒,原理获取凭证(只有一个)

代码实现,参考地址https://blog.csdn.net/zhaomengxia123/article/details/126598141

1.LockSupprot阻塞与唤醒顺序没有要求吗?为什么连续唤醒两次,阻塞两次,最后仍是阻塞?

 因为unpark获取了一个凭证,之后再调用park方法,就可以名正言顺的凭证消费,故不会阻塞。
 因为凭证的数量最多为1,连续调用两次unpark和调用一次unpark效果一样,只会得到一个凭证;而调用两次park却需要消费两个凭证,证不够,不能放行。

三、互斥锁、信号量、多线程等待机制

1) 互斥锁:一个资源只能被一个线程访问,ReentranLock使用tryLock()获取锁,失败返回false;unlock()释放锁
2) 信号量:指定个线程同时访问某个资源。相当于计数器,获取这个资源-1. 创建new Semaphore(n),semaphore.acquire()获取许可,semaphore.release()释放许可
3) 等待机制:等待指定个线程达到某些条件后,进行下一步,类似开会。创建new CountDownLatch(n),countDown()计数器减一,await()主线程唤醒。

代码实现,参考地址https://www.php1.cn/detail/XiangJie_java_Zh_07a2d934.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈年_H

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值