Java并发
墨玉浮白
这个作者很懒,什么都没留下…
展开
-
volatile对原子性、可见性、有序性的保证
1.原子性volatile对原子性的保障有限,32位jvm中的long、double类型的变量赋值操作不是原子的,volatile可以保证它俩的原子性。2.可见性、有序性volatile在写操作的前后加上Release屏障、Store屏障;在读操作前后加Load屏障、Acquire屏障。volatile boolean isRunning = true;//线程1Release屏障,保证写和上面的读写不会指令重排isRunning = false;Store屏障,保证写完后会立马执行flu原创 2021-08-09 09:44:15 · 519 阅读 · 0 评论 -
synchronized同时对原子性、可见性、有序性的保证
原子性:基本复制写操作都能保证原子性,复杂操作无法保证可见性:MESI协议的flush、refresh配合使用,解决可见性有序性:3个层次,最后1个层次有4中内存重排序synchronized可同时保证:原子性:有加锁和释放锁的机制,加锁后,同一段代码只有他能执行可见性:加内存屏障,在同步代码块做变量写操作,在释放锁时,会强制执行flush操作。在获取锁进入同步代码块时,会对变量读强制执行refresh操作。有序性:加各种内存屏障,解决4种内存重排序1.synchronized保证原子性的原创 2021-08-09 09:43:16 · 1978 阅读 · 1 评论 -
ThreadLocal内存泄漏问题
ThreadLocal可以存取各线程专属的变量副本,底层是通过ThreadLocalMap实现,存的K-VK:WeakReference弱引用V:变量副本一旦某线程长期存活,但ThreadLocal里的K是弱引用,万一内存不够,进行GC,就会自动把很多线程寄存在ThreadLocal中的key(弱引用)进行回收。Key —> Value 就成了 null —> Value此时已经没办法获取到Value了,但是Value还在真真实实的占用内存空间。于是就内存泄漏了JDK团队的解决方原创 2021-08-09 09:41:49 · 188 阅读 · 0 评论 -
内存屏障在硬件层面的实现原理
解决可见性问题Store屏障:强制要求对一个写操作,必须阻塞等待到其他处理器返回invalidate ack后,对数据加独占锁。强制执行flush操作,修改数据到高速缓存或主内存中Load屏障:从高速缓存中读取数据时,如果发现无效队列中有invalidate消息,强制将自己高速缓存的数据状态设为 I过期。然后执行Refresh操作,发送read消息,从其他处理器的高速缓存中刷新最新的值解决有序性问题Acquire屏障、Release屏障 = StoreStore屏障 + StoreLoad屏障…可以原创 2021-08-09 09:41:13 · 365 阅读 · 0 评论 -
MESI协议为何会引发 有序性、可见性的问题
引发可见性case1:处理器将数据写入到写缓冲器,发送invalidate消息到总线bus,就认为成功了。此时数据并未到高速缓存中。case2:处理器1在嗅探到invalidate之前,就去读取高速缓存的数据。如果有,就从自己高速缓存中取出旧值;如果没有,就发送read消息,去处理器0的高速缓存读。处理器0提供的就是旧值。case3:处理器1收到invalidate消息,放到无效队列。还没来得及将自己的数据状态设为 I过期。此时读,就是旧值原因:写缓冲器+无效队列 导致的。写数据不一定立马写入自己的原创 2021-08-09 09:40:29 · 348 阅读 · 0 评论 -
写缓冲器 + 无效队列,优化MESI协议的性能
MESI协议,处理器0想写一条数据,需要向总线bus发送invalidate消息,并确认收到invalidate ack消息后,才能动手执行写操作。读阻塞,效率低。处理器将数据写入到写缓冲器,发送invalidate消息出去,就认为成功了、去干别的事了。处理器1嗅探到invalidate消息,将invalidate放到无效队列中,直接返回invalidate ack。过段时间,再从无效队列取出invalidate消息、消费,将自己的状态设为 I过期 。收到所有 处理器return的invalidate原创 2021-08-09 09:38:32 · 445 阅读 · 0 评论 -
MESI缓存一致性协议
每个处理器,在自己的高速缓存中都有变量副本,这就有了缓存不一致问题,因此有了MESI协议。MESI协议将缓存行状态flag分为4种:invalid:无效的,标记为I,当前cache entry无效,数据不能使用shared:共享的,标记为S,当前cache entry有效,数据在各个处理器中都有副本,且副本值都和主内存一致exclusive:独占的,E,当前处理器对这个数据独占,只有它有数据副本,其他处理器没有modified:修改过的,M,只能有1个处理器对共享数据更新处理器0读取某个变原创 2021-08-09 09:37:08 · 635 阅读 · 1 评论 -
高速缓存的数据结构:拉链散列表
高速缓存的底层数据结构:拉链散列表,很多bucket,挂了很多cache entry(tag + cache line + flag)。cache line就是缓存的数据,包含多个变量的值tag指向了缓存数据在主内存中的地址flag表示了缓存行的状态处理器读取高速缓存时,会根据变量名执行内存地址解码的操作。解析出 index(定位到bucket) + tag(定位到cache entry) + offset(定位到变量在cache line的位置)如果能成功定位到高速缓存中的数据,且flag.原创 2021-08-09 09:35:34 · 314 阅读 · 1 评论 -
CAS原子操作
前言CAS是一种乐观锁思想的应用,所以在介绍CAS之前,需要先了解一下何为乐观锁、何为悲观锁?乐观锁与悲观锁众所周知,CPU是时分复用的。也就是把CPU的时间片,分配给不同的thread/process轮流执行。时间片之间需要进行CPU切换,也就是会发生进程的切换。这里的切换,会涉及到清空寄存器、缓存数据,然后重新加载新的thread所需数据。当一个线程被挂起时,加入到阻塞队列,在一定的时间...原创 2019-05-06 15:28:49 · 1173 阅读 · 0 评论 -
volatile关键字
前言如果为了读写一两个实例域就使用同步,未免有点小题大做,内存开销太大。volatile关键字是为实例域的同步访问提供的免锁机制,若声明一个域为volatile,编译器、虚拟机就会得知该域可能会被另一线程并发更新。这里,我们需要了解java内存模型及并发编程的3大特性:原子性、可见性、有序性1、内存模型...原创 2019-05-06 14:22:07 · 228 阅读 · 0 评论 -
生产者、消费者模式实现
1、1生产和1消费一个String对象,生产者设定值,消费者拿走值。wait()/notify()实现生产者public class Product { private String lock; public Product(String lock) { super(); this.lock = lock; } public...原创 2019-04-30 15:47:04 · 159 阅读 · 0 评论 -
线程间通信的两种方式
1、wait()/notify()Object类中相关的方法有notify和wait方法,又因为它俩被定义在Object类中,故会被所有的类继承。它俩都是final的,不能被重写,不能通过子类重写改变。wait()方法让当前线程进入等待,并释放锁注意:(1)当前线程必须拥有当前对象的monitor,也就是lock锁。这样才能调用wait()方法,否则会抛出异常 (2)线程调用wait方法...原创 2019-04-30 15:12:20 · 260 阅读 · 0 评论 -
多线程编程之线程池
一、概述在编程中经常会用到线程来处理异步任务,但每个线程的创建、销毁都需要一定的开销。如果每次执行一个任务都要创建一个线程,就会销毁大量资源,并且线程之间“互不干扰”很难管理。这种情况下,就要用到线程池来对线程进行统一管理了。在创建线程的3种方式中的第3种实现Callable接口的例子中,其实就已经实现了一个单线程化的线程池。二、ThreadPoolExecutor创建线程池java5提供的...原创 2019-04-24 14:01:19 · 184 阅读 · 0 评论 -
Java并发之synchronized关键字
简介原创 2019-04-26 14:10:35 · 178 阅读 · 0 评论 -
Java并发之ReentrantLock锁
简介一种可重入的互斥锁,经由Java5引入,支持一个线程对资源的重复加锁。它和synchronized语句和方法访问的隐式监视器锁,有相同的基本行为和语义,但是功能更强大。一、Lock接口锁对象,在java中锁是控制多个线程访问共享资源的方式,一个锁能防止多个线程同时访问共享资源(但有的锁就允许多个线程并发访问,如读写锁)。在Lock接口出现之前,是靠synchronized关键字来实现锁的...原创 2019-04-26 15:12:27 · 863 阅读 · 0 评论 -
Java并发之公平锁
概述CPU在调度线程的时候,会在等待队列里随机挑选一个线程。由于随机性,故不能保证线程先到先得(synchronized控制的锁就是这种非公平锁)。这样就会产生饥饿现象,即有些优先级较低的线程可能永远无法取得CPU的执行权,优先级较高的线程会不断抢占资源。于是,就有了公平锁。公平锁可以保证线程按照时间顺序执行,避免饥饿现象的产生。但是公平锁的效率很低,因为要保证顺序执行,就得维护一个有序队列。...原创 2019-04-26 15:26:01 · 645 阅读 · 0 评论 -
synchronized和ReentrantLock区别
1、区别:1)Lock是一个接口,synchronized是Java中的关键字,synchronized是内置的语言实现;2)synchronized发生异常时,会自动释放线程占用的锁,故不会发生死锁现象。Lock发生异常,若没有主动释放,极有可能造成死锁,故需要在finally中调用unLock方法释放锁;3)Lock可以让等待锁的线程响应中断,使用synchronized只会让等待的线程...原创 2019-04-26 16:08:30 · 15450 阅读 · 3 评论 -
Java并发之同步的产生及解决
同步问题的产生由于Java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时,就会有相互冲突而导致的数据不准确问题。卖火车票案例public class Ticket implements Runnable { private int num = 100;//假设一共有100张票 @Override public void run() { w...原创 2019-04-26 16:09:26 · 133 阅读 · 0 评论 -
Java创建线程的3种方式
前言多线程的实现一般有3中方法,其中前两种最常用。原创 2019-04-22 16:23:22 · 180 阅读 · 0 评论 -
线程池之FixedThreadPool学习
简介FixedThreadPool是可重用固定线程数的线程池。创建方法public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0...原创 2019-04-24 13:03:55 · 10020 阅读 · 0 评论 -
线程池之CachedThreadPool学习
简介CachedThreadPool是一个根据需要创建线程的线程池。创建方法public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, Ti...原创 2019-04-24 13:23:52 · 16773 阅读 · 0 评论 -
线程池之SingleThreadPool学习
简介SingleThreadPool是使用单个工作线程的线程池。创建方法public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, ...原创 2019-04-24 13:36:54 · 7765 阅读 · 0 评论 -
线程池之ScheduledThreadPool学习
简介ScheduledThreadPool是一个能实现定时、周期性任务的线程池。创建方法 public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); }这...原创 2019-04-24 13:57:51 · 11373 阅读 · 1 评论 -
Java并发之死锁
前言在Java编程中,有3种典型的死锁类型:静态的锁顺序死锁、动态的锁顺序死锁、协作对象之间的死锁一、静态的锁顺序死锁a、b两个方法都要获得A锁和B锁。线程1执行a方法获得了A锁,在等待B锁;线程2执行了b方法获得了B锁,在等待A锁。/***可能会发生静态的锁顺序死锁*/public class StaticLockOrderDeadLock { private final ...原创 2019-04-25 20:40:12 · 315 阅读 · 0 评论