高并发学习养成计划3

阻塞队列和生产者-消费者模式

该模式是将“找出需要完成的工作”与“执行工作”两个过程分离开来,并把工作放到“待完成”列表以便在随后处理,而不是找出后立即处理。生产者消费者模式能简化开发过程,因为它消除了生产者类和消费者类之间的代码依赖性,此外,该模式还将生产数据的过程与使用数据的过程解耦开来以简化工作负载的管理,因为这两个过程在处理数据的速率上有所不同。在某些情况下,需要调整生产者线程数量和消费者线程数量之间的比率,从而实现更高的资源利用率。在构建高可靠的应用程序时,有界队列是一种强大的资源管理工具:他们能抑制并防止产生过多的工作项,使应用程序在负荷过载的情况下变得健壮。

虽然该模式将消费者和生产者解耦开来了,但是却和工作队列耦合了。开发人员一般会认为消费者处理工作的速率能赶上生产者,因此通常不会考虑队列的大小边界,这将导致在以后将重新设计系统架构。阻塞队列作为工作队列,使得问题更加简单。如果阻塞队列不满足需求,还可以使用Semaphore来创建其他的阻塞数据结构。

 

BlockingQueue

其并不是一个真正的队列。因为它不会为队列中的元素维护存储空间。它维护的是一组线程。这些线程在等待着把元素加入或移出队列。传统的队列是必须等带一个数据首先完成入列和出列等操作。(个人想法:该容器是将所有消费者线程进行维护,当生产者线程处理完数据后,就将数据放入队列中维护的某个消费者线程中)。由于该容器没有存储功能,一次put和take会一直阻塞,直到有另一个线程已经准备好到交付过程中。所以当有足够多的消费者线程,并且总有一个消费者准备好接受数据,才适合使用同步队列。

 

串行线程封闭

生产者-消费者模式和阻塞队列一起,促进了串行线程封闭。从而将生产者数据的所有权安全的转交给消费者。再转交了所有权后,只有接受的线程对数据进行所有权,转交的线程没有。这种安全的发布,确保了对象再发布后从而被封闭在一个新的线程中。对象池利用了串行线程的封闭,将对象借给一个请求线程。

 

双端队列和工作密取

Deque双端队列实现了队列头和队列尾的高效插入与删除。该队列的原理设计非常的巧。每个消费者都有自己的队列,当某个线程完成了对自己队列的操作,那么他可以秘密的从另外某个队列的尾部获取数据进行处理,从而进一步降低了队列山的竞争,确保了每个线程都保持在忙碌状态。

使用场景:

1  .在网页爬虫处理一个页面时,通常会发现有更多的页面需要处理

  1. 在垃圾回收阶段对堆进行标记,都可以通过工作密取的机制来实现高效的并行

 

阻塞方法和中断方法

线程可能会阻塞或暂停执行,原因有很多:等待IO操作结束,等待获取一个锁,等待从sleep方法中醒来等。当线程阻塞是,它通常被挂起,并处于某种阻塞状态(BLOCKED、WAITING、TIMED_WAITING)。(个人说明:该三种阻塞状态是由区别的。BLOCKED:这种状态是指一个阻塞线程在等待monitor锁。WAITING:一个线程在等待另一个线程执行一个动作时在这个状态。TIMED_WAITING:一个线程在一个特定的等待时间内等待另一个线程完成一个动作会在这个状态)

Thread中的interrupt用于中断线程(其实也就是改变Thread的中断状态参数)。中断是一种协作机制,一个线程不能让在执行操作的另一个线程立即中断,而是要求另一个线程执行到可以中断的地方进行中断。

当调用了一个InterruptedException异常的方法时,你自己的方法也将变成一个阻塞方法,并且必须要处理对中断的响应。对于库代码来说有两个基本的选择:

  1. 传递IE:不捕获异常,把异常传递给方法的调用者(最明智)
  2. 恢复中断:当不能抛出异常时,这时捕获异常,并调用当前线程的interrupt方法恢复中断状态,这样在调用栈中更高层的代码将看到引发一个中断(这里看了很久没有搞懂)。

 

 

同步工具类

同步工具类可以是任意的一个对象,只要它根据自身的状态来协调线程的控制流。同步工具类有阻塞队列,信号量,闭锁,栅栏。

 

 

闭锁

类似于一个门,门开始是关闭的,让线程先来门前等着,当所有线程都来到门前,此时打开门执行下面的语句。当闭锁这道门打开后,将不会再次关闭,将永远保持打开。

CountDownLatch是一种灵活的闭锁实现,闭锁的状态包括一个计数器,该计数器先初始化为一个正数,countDown方法递减计数器,表示一个事件已经发生了,而await方法是一直阻塞到计数器为0(此时就代表所有线程集合完毕。开门,放狗)。

 

下面使用TestHarness类来演示闭锁:

 

代码详解:在timeTask方法的功能试问了测试多个线程都做完任务化的时间。在该方法中设置了一个启动门和一个结束门。启动门的作用:是为了防止System.nanoTime(),在没执行的时候已经有线程开始自己的任务了,这样就不能的到一个较为精确的值。结束门的作用是让所有线程执行结束后,再执行System.nanoTime().。这样得出来的程序的运行时间是比较精确的。

 

信号量

信号量是用来控制同时访问某个特定资源的操作数量,或者同时执行某个指定操作的数量。使用信号量可以让任何一种容器变为有界阻塞容器。例如使用信号量构建的数据库连接池,当池为空时获取连接的线程阻塞。不为空时,从池中拿取一个连接时,首先要调用Semaphore的acquire方法,在资源返回池中时,再调用release即可。

 

使用信号量来构建一个有界阻塞容器

 

代码详解:略

 

栅栏

栅栏类似于闭锁,但不同于闭锁。

  1. 闭锁用于等待事件,栅栏用于等待线程(闭锁是要所有的事件都执行完成,才放行所有的线程,而栅栏是要所有的线程都到达“等待地”,就会放行。)
  2. 闭锁是一次性对象,一旦终止不能被重置。栅栏可以反复使用。

 

当线程到达栅栏位置时将会调用await方法,这个方法将阻塞,直到所有的线程都到达此处就会被释放。如果await方法调用超时或者await阻塞的线程被中断,那么栅栏就被打破了,所有阻塞的await调用都将终止并且抛出BrokenBarrierExceptionyichang1。如果成功的通过栅栏,那么await将为每一个线程返回一个唯一的到达索引号,我们可以利用这些索引号来“选举“产生一个领导线程,并在下一次迭代中由该领导线程执行一些特殊的工作。(索引号一直搞不懂有什么用)

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值