《Java并发编程实战》读书笔记——volatile(可见性、重排序和内存屏障)、Exchanger和CompletionService

1、volatile,保证了可见性,禁止指令重排序。

    (1)可见性,深入分析volatile的实现原理

    处理器先将内存数据读到L1、L2缓存,再进行操作,不定期写回内存。如果写volatile变量,JVM就会向处理器发送一条Lock前缀的指令,Lock指令会使处理器将缓存写入内存,并使其他处理器的缓存无效。但其他处理器缓存的值还是旧的,为了保证各个处理器的缓存一致,就会实现缓存一致性协议,每个处理器通过嗅探在总线上传播的数据来检查自己的缓存是否过期,当处理器发现缓存被修改,就会使缓存失效,重新从内存读取。

    (2)指令重排序和happens-before原则

    为了充分利用多核和多级缓存,在不影响单线程执行结果的前提下,编译器和处理器可以进行指令的重排序。

    happens-before原则是Java内存模型下的一些“天然的”先行发生关系,有顺序性保障。

    【1】程序次序规则,在一个线程内,按照程序代码顺序,前面的操作先行发生于后面的操作(无法禁止编译重排和指令重排);

    【2】管程锁定规则,一个unlock操作先行发生于后面对同一个锁的lock操作(synchronized);

    【3】volatile变量原则,对一个volatile变量的写操作先行发生于后面对这个变量的读操作

    【4】线程启动规则,Thread对象的start()方法先行发生于此线程的每一个动作;

    【5】线程终止规则,线程中的所有操作都先行发生于对此线程的终止检测;

    【6】线程中断规则,对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生;

    【7】对象终结规则,一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始;

    【8】传递性

    (3)内存屏障volatile的使用及其原理

    为了实现volatile可见性和happen-before的语义。JVM底层是通过一个叫做“内存屏障”的东西来完成。内存屏障,也叫做内存栅栏,是一组处理器指令,用于实现对内存操作的顺序限制。下面是完成上述规则所要求的内存屏障:

    

    【1】LoadLoad 屏障
    执行顺序:Load1—>Loadload—>Load2
    确保Load2及后续Load指令加载数据之前能访问到Load1加载的数据。

    【2】StoreStore 屏障
    执行顺序:Store1—>StoreStore—>Store2
    确保Store2以及后续Store指令执行前,Store1操作的数据对其它处理器可见。

    【3】LoadStore 屏障
    执行顺序: Load1—>LoadStore—>Store2
    确保Store2和后续Store指令执行前,可以访问到Load1加载的数据。

    【4】StoreLoad 屏障
    执行顺序: Store1—> StoreLoad—>Load2
    确保Load2和后续的Load指令读取之前,Store1的数据对其他处理器是可见的。


    综上,写volatile变量时,使用lock指令强制处理器将变量写回内存,并使其它处理器的缓存失效,保证volatile的可见性;volatile满足happens-before原则,可以禁止重排序,使用内存屏障实现(实现对内存操作的顺序限制)


2、Exchanger

    用于两个工作线程交换数据,一个线程到达exchange()方法调用点时,如果伙伴已经调用过了exchange(),则交换数据;否则挂起等待伙伴线程。用slot数组(为了提高并发度)实现,线程到达时,如果slot内没有线程在等待,则去index /= 2的slot里去查找,如果slot里一直没有线程在等待,最终会到达slot[0]。


3、CompletionService,融合了Executor和BlockingQueue的功能

public interface CompletionService<V> {

	Future<V> submit(Callable<V> task);

	Future<V> submit(Runnable task, V result);

	Future<V> take() throws InterruptedException;

	Future<V> poll();

	Future<V> poll(long timeout, TimeUnit unit) throws InterruptedException;
}
// 节选自jdk1.8
public class ExecutorCompletionService<V> implements CompletionService<V> {

	// Executor + BlockingQueue实现
	private final Executor executor;

	private final AbstractExecutorService aes;

	private final BlockingQueue<Future<V>> completionQueue;

	// 提交任务时,先包装成QueueingFuture
	private class QueueingFuture extends FutureTask<Void> {
		QueueingFuture(RunnableFuture<V> task) {
			super(task, null);
			this.task = task;
		}

		// 改写了FutureTask的done()方法(原本是空实现),将结果放入阻塞队列
		protected void done() {
			completionQueue.add(task);
		}

		private final Future<V> task;
	}

	public Future<V> take() throws InterruptedException {
		return completionQueue.take();
	}
}
    使用场景:如果提交了一组任务,希望完成后获得结果,可以保留ExecutorService返回的List<Future>,轮询list,过于繁琐。CompletionService解决了这个问题,按照任务完成的先后顺序将Future放入队列,直接从阻塞队列取即可。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值