什么是阻塞队列?阻塞队列的实现原理是什么?如何使用阻塞队列来实现生产者-消费者模型?乐观锁和悲观锁的理解及如何实现,有哪些实现方式?什么是Daemon线程?它有什么意义?

本文详细介绍了阻塞队列的工作原理及其在生产者消费者模型中的应用,列举了JDK提供的不同阻塞队列类型,并对比了乐观锁和悲观锁的概念及其实现方式,最后讨论了Daemon线程的作用和特点。
摘要由CSDN通过智能技术生成
什么是阻塞队列?阻塞队列的实现原理是什么?如何使用阻 塞队列来实现生产者 - 消费者模型?
阻塞队列( BlockingQueue )是一个支持两个附加操作的队列。
这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元
素的线程会等待队列可用。
阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿
元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。
JDK7 提供了 7 个阻塞队列。分别是:
ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。
LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。
PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。
DelayQueue :一个使用优先级队列实现的无界阻塞队列。
SynchronousQueue :一个不存储元素的阻塞队列。
LinkedTransferQueue :一个由链表结构组成的无界阻塞队列。
LinkedBlockingDeque :一个由链表结构组成的双向阻塞队列。
Java 5 之前实现同步存取时,可以使用普通的一个集合,然后在使用线程的协作和线程同步可以实
现生产者,消费者模式,主要的技术就是用好, wait ,notify,notifyAll,sychronized 这些关键字。而
java 5 之后,可以使用阻塞队列来实现,此方式大大简少了代码量,使得多线程编程更加容易,
安全方面也有保障。
BlockingQueue 接口是 Queue 的子接口,它的主要用途并不是作为容器,而是作为线程同步的的工
具,因此他具有一个很明显的特性,当生产者线程试图向 BlockingQueue 放入元素时,如果队列已
满,则线程被阻塞,当消费者线程试图从中取出一个元素时,如果队列为空,则该线程会被阻塞,
正是因为它所具有这个特性,所以在程序中多个线程交替向 BlockingQueue 中放入元素,取出元
素,它可以很好的控制线程之间的通信。
阻塞队列使用最经典的场景就是 socket 客户端数据的读取和解析,读取数据的线程不断将数据放入
队列,然后解析线程不断从队列取数据解析。
乐观锁和悲观锁的理解及如何实现,有哪些实现方式?
悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候
都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。
传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做
操作之前先上锁。再比如 Java 里面的同步原语 synchronized 关键字的实现也是悲观锁。
乐观锁:顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是
在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。
乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于 write_condition
制,其实都是提供的乐观锁。
Java java.util.concurrent.atomic 包下面的原子变量类就是使用了乐观锁的一种实现方式 CAS
现的。
乐观锁的实现方式:
1 、使用版本标识来确定读到的数据与提交时的数据是否一致。提交后修改版本标识,不一致时可
以采取丢弃和再次尝试的策略。 2 java 中的 Compare and Swap CAS ,当多个线程尝试使用 CAS 同时更新同一个变量时,只有其
中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争
中失败,并可以再次尝试。 CAS 操作中包含三个操作数 —— 需要读写的内存位置( V )、进行比
较的预期原值( A )和拟写入的新值 (B) 。如果内存位置 V 的值与预期原值 A 相匹配,那么处理器会自
动将该位置值更新为新值 B 。否则处理器不做任何操作。
CAS 缺点:
1. ABA 问题: 比如说一个线程 one 从内存位置 V 中取出 A ,这时候另一个线程 two 也从内存中取出
A ,并且 two 进行了一些操作变成了 B ,然后 two 又将 V 位置的数据变成 A ,这时候线程 one 进行
CAS 操作发现内存中仍然是 A ,然后 one 操作成功。尽管线程 one CAS 操作成功,但可能存在
潜藏的问题。从 Java1.5 开始 JDK atomic 包里提供了一个类 AtomicStampedReference 来解决
ABA 问题。
2. 循环时间长开销大: 对于资源竞争严重(线程冲突严重)的情况, CAS 自旋的概率会比较大,
从而浪费更多的 CPU 资源,效率低于 synchronized
3. 只能保证一个共享变量的原子操作: 当对一个共享变量执行操作时,我们可以使用循环 CAS
方式来保证原子操作,但是对多个共享变量操作时,循环 CAS 就无法保证操作的原子性,这个
时候就可以用锁。
什么是 Daemon 线程?它有什么意义?
所谓后台 (daemon) 线程,也叫守护线程,是指在程序运行的时候在后台提供一种通用服务的线程,
并且这个线程并不属于程序中不可或缺的部分。
因此,当所有的非后台线程结束时,程序也就终止了,同时会杀死进程中的所有后台线程。反过来
说, 只要有任何非后台线程还在运行,程序就不会终止。
必须在线程启动之前调用 setDaemon() 方法,才能把它设置为后台线程。注意:后台进程在不执行
finally 子句的情况下就会终止其 run() 方法。
比如: JVM 的垃圾回收线程就是 Daemon 线程, Finalizer 也是守护线程。
  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值