多线程(四)

文章介绍了多线程中的两种设计模式——单例模式(饿汉模式和懒汉模式)及其线程安全问题,强调了静态修饰符在实现单例模式中的作用。此外,详细讲解了阻塞队列的概念、线程安全性和在生产者消费者模型中的应用。最后,探讨了标准库中的定时器以及自定义定时器的实现步骤,包括任务管理、优先级队列和线程同步策略。
摘要由CSDN通过智能技术生成

1、多线程的案例

 其设计模式有多种,我们重点学习两种:单例模式和工厂模式。

2、案例一:单例模式 

单例:指的是单个实例(或单个对象)。比如,在之前数据库的学习中,接触到的JDBC中,有个DataSource数据源,它就可作为一个单例, 这种需求就是在一个数据库的操作中,有一份数据源就足够了。

单例模式:就是借助编程语言语法的特性,强行限制某个类不能创建多个特性。 

如何实现=》强行限制某个类:借助static。static修饰的成员或者属性都是类成员/类属性。属性变为类属性,此时就是“单个实例”。

(1)饿汉模式

 下面是单例模式的一种实现=》称:饿汉模式。

其中,用static保证该实例为单个实例;

其次,类外如果访问该实例,只能通过一个get方法得到

最后,为了防止在类外可以new一个该类对象 ,将构造方法设置为私有的

 =》饿汉模式的创建时间:类加载的阶段。创建时机早。

=》但懒汉模式,创建的时机更迟。

(2)懒汉模式 

 =》如果整个代码后序,没有调用getInstance方法,这样就节省构造实例的过程;

=》如果后序调用的比较晚,可以与其他操作岔开。【还有个好处:一般程序刚启动,初始化的东西会很多,系统资源紧张;这样的话也会缓解】

 (3)、两个单例模式是否线程安全?

要看线程是否安全,即就是看多个线程同时调用getInstance()方法,是否会出现问题?

(1)饿汉模式=》线程安全 

 (2)懒汉模式=》线程不安全

 a)如何解决懒汉线程不安全?

方法:加锁,把多个操作打包成一个原子操作。

但是如下,加锁的位置,仍不能保证原子性操作。 

 

将锁的范围扩大:

t2的load在t1完成结束后进行

 

 但是又出现新的问题:

上述懒汉模式的线程不安全问题,只是在创建实例之前(即首次调用的时候)才会触发。一旦线程创建好了,就线程安全了。

而我们加锁,则是一直都在加锁,即使线程安全了也一样。

=》加锁的开销大【可能会涉及到用户态与内核态的之间切换,这样的切换成本也大】

优化: 在加锁的外层,再加上一层判定条件

 又一问题:

优化: 针对指令重排序问题,使用volatile

 一个完整的,线程安全的懒汉模式:

3、案例二:阻塞队列

 阻塞队列是一种特殊的队列。

=》阻塞队列是线程安全的

=》带有阻塞功能:当队列满,继续入队列,入队列的操作就会阻塞;直至队列不满,入队列才能完成。        当队列空,继续出队列,出队列操作就会阻塞,直至队列不空,出队列才能完成。

=》应用场景:生产者消费者模型【描述多线程协同工作的一种方式】。 

 =》其他用处【优势】:

        ->:使用阻塞队列,有利于代码“解耦合”

        ->:削峰减谷 :阻塞队列相当于一个缓冲区,平衡了生产者与消费者之间的处理能力。

                                 比如,突发事件容易引起流量暴增。如果使用阻塞队列,此时流量骤增时,A队列承受了压力,B、C还是按照原来的节奏消费数据,对它两的冲击不大。

(1)标准库中的阻塞队列 

=》其中,BlockingQueue只是一个接口,其有多种实现方式。 

=》阻塞式的入队、出队,对应方法---->put  ;  take

=》其他方法,如offer,poll,peek等,不带阻塞特性。 

 (2)自己实现阻塞队列

step1:实现一个普通的循环队列

 step2:上面出队,入队需要进行大量的修改操作,多个线程工作时,线程不安全;因此,需要进行加锁操作,同时,为了保证内存可见性问题,对变量加上volatile关键字

 step3:实现阻塞操作

原则:当入队时,如果队列满了,就阻塞;[当有出队列操作成功时,就notify通知]

           当出队时,如果队列空了,就阻塞;

 唤醒之后,需要再次判断一下,看是否真的满足条件了

 因此此处使用的是while.

 对阻塞队列=》生产者消费者模型的一个举例:

 4、案例三:定时器

 (1)标准库中的定时器 

 效果:

 我们可以看出,执行完上述任务后,进程并没有退出。

Timer内部需要一组线程来执行注册任务。而这里的线程是 前台线程,会影响进程退出。

 (2)自己实现定时器

step1: 安排的任务【描述这个任务】,任务包含两方面的信息,一个是要执行啥工作,一个是啥时候执行;

step2:看下如何让MyTimer管理多个任务 

=》采用优先级队列,不是线程安全的,使用BlockingQueue来实现

 

step3:任务已经被安排到优先级阻塞队列了,然后需要从队列取元素

 =》创建一个单独的扫描线程,让这个线程一直检查队首元素,看时间是否到了;如果时间到了,则执行该任务。

 step4:运行上述代码,出错了。比较的对象mytask,是不对的。所以必须自己实现一个比较的规则。

实现Comparable接口,重写compareTo方法。

 最终的结果展示:

step5: 上述会出现忙等的情况

 假设现在12点,定2:30的闹钟。此时CPU并没有被空闲出来,这里就会一直忙等。

=》假设sleep两个小时,这样也是不行的。假设在1点时插入个新任务,这样就无法完成。

 ->使用wait解决上述忙等问题

 但目前还有问题,take操作和后续的判断,put,wait不是原子的。=》解决:加锁。

 修改之后,这里的notify就不会出现在take和wait之间了。

结果:

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值