精华内容
下载资源
问答
  • 1.文件内容读取到缓存 2.从缓存获取文件内容,写入到磁盘 3.控制缓存大小 4.多线程并发写丶读
  • java——多线程并发

    2019-07-11 14:59:00
    JDK5中增加了Doug Lea的并发库,这一引进给...该包提供了线程的运行,线程池的创建,线程生命周期的控制. Java通过Executors提供四个静态方法创建四种线程池,分别为: newCachedThreadPool创建一个可缓存线程...

     

    JDK5中增加了Doug Lea的并发库,这一引进给Java线程的管理和使用提供了强大的便利性。 java.util.current包中提供了对线程优化、管理的各项操作,使得线程的使用变得的心应手。该包提供了线程的运行,线程池的创建,线程生命周期的控制.

     

    Java通过Executors提供四个静态方法创建四种线程池,分别为:

    newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

    newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

    newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。

    newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行

    名词解释:

    FIFO: First in, First out.先进先出。

    LIFO: Last in, First out.后进先出。

     

    转载于:https://www.cnblogs.com/Tpf386/p/11170035.html

    展开全文
  • as-if-serial语义happens-before重排序数据依赖性和控制依赖性数据依赖性控制依赖性内存屏障LoadLoad屏障StoreStore屏障LoadStore屏障StoreLoad屏障java中内存屏障使用volatile获取锁和释放锁final域顺序一致性...


    在JAVA多线程并发中有三个性质,分别为原子性、可见性、有序性,接下来将会介绍java多线程并发时是如何满足这三个性质的。

    JMM是什么

    首先介绍JMM(java内存模型)是什么,JMM定义了一种内存模型来屏蔽各个硬件平台和操作系统的内存访问差异,已实现java程序在各个平台下都能达到一致的内存访问效果。
    在java中,所有的实例域、静态域和数组元素都存储在堆内存中,堆内存在线程之间共享,这被称为共享内存。线程之间的共享变量存储在主内存之中,每个线程的工作内存存储了共享变量的副本。

    就如下图所示一样,每个线程的工作内存是相互隔离的,当线程对自己的工作内存中的共享变量进行修改时,其他线程是如何感知这个变量的值已经被修改了呢?这个问题就是多线程并发编程的可见性的问题。

    如何解决可见性?

    1. 通过同步互斥的方式实现,也就是我们所说的锁。当加锁时,JMM会把该线程对应的本地内存置为无效,从而使得被监视器保护的临界区代码必须从主内存中读取共享内存;当释放锁时,JMM会把线程对应的本地内存中的共享内存刷新到主内存中。
    2. 如果觉得加锁的方式过于“重”了,也可以选择使用volatile修饰变量。在写一个volatile变量时,JMM会把线程对应的本地内存中的共享变量刷新到主内存中;在读一个volatile变量时,JMM会把线程对应的本地内存置为无效,线程接下来从主内存中读取共享变量。

    volatile的底层实现

    volatile通过汇编中的lock指令将当前处理器缓存行当数据写回到系统内存,这个写回内存的操作会使在其他CPU里缓存了该内存地址的数据无效。为了提高处理器的速度,处理器不直接和内存通信,而是先将系统内存写入处理器缓存再进行操作。当对一个volatile变量进行写操作时,会对处理器发送一条lock指令,处理器就将处理器缓存中的数据写回到系统内存。在多处理器下,为保证多处理器中的缓存的数据都是一致的,会使用内存一致性协议,每个处理器嗅探总线上传播的数据来检查自己的数据是否被更改,当处理器发现自己缓存行对应的内存地址被修改,就将这个内存地址置为无效,当处理器要对这个数据进行操作时,就从系统内存中读取这个数据到自己的缓存中。

    如何解决原子性?

    线程的原子操作是不可被中断的一个或一系列操作,处理器提供总线锁定和缓存锁定两个机制来保证复杂内存操作的原子性。

    通过总线锁定保证原子性

    使用处理器提供的lock#信号,当处理器在总线上输出这个信号时,其他处理器的请求会被阻塞住,那么该处理器可以独占共享内存。

    通过缓存锁定保证原子性

    通过总线锁定原子性开销比较大,当共享内存被锁定时,其他处理器不能操作其他内存地址的数据。所以处理器在某些场合下使用缓存锁定代替总线锁定。缓存锁定就是处理器不输出lock#信号,而是更改内部的内存地址,使用缓存一致性原则保证操作的原子性。

    java中通过锁和循环CAS保证原子性

    使用循环CAS实现原子操作

    自旋CAS的基本思路就是循环进行CAS操作知道成功为止。CAS需要两个数值,一个旧值,一个新值,在操作前先比较旧值有无发生变化,若无变化则交换成新值,若有变化则不交换。

    CAS实现原子操作的三大问题
    1. ABA问题
      因为CAS需要比较旧值是否发生变化,如果没有变化则更新,如果一个变量值为A,若A线程B线程同时操作这个变量,线程A将这个值写为B,再写为A,线程B操作这个变量时会认为这个变量并没有发生变化,但是实际上发生了变化,这就是ABA问题。
      在Atomic包中的AtomicStampedReference解决了ABA问题,这个类的compareAndSet方法的作用是首先检查当前引用是否等于预期引用,并且检查当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。
    2. 循环时间长开销大
      如果自旋CAS长时间不成功,会给CPU带来非常大的执行开销。
    3. 只能保证一个共享变量的原子操作
      对多个共享变量进行操作时,循环CAS就无法保证操作的原子性。JDK提供了AtomicReference类来保证引用对象之间的原子性,就可以把多个变量放在一个对象里进行CAS操作。

    如何解决有序性?

    什么是有序性?
    在单线程下,编译器和处理器会在不改变程序执行结果的前提下,对程序和指令执行顺序进行重新排序,以此优化程序执行效率(这遵守了as-if-serial语义)。但在多线程下,这种重排序会影响程序执行的结果。
    在JMM中,JMM天生具有一定的有序性,这是因为JMM中,如果一个程序的操作执行结果需要对另一个操作可见,那么这两个操作必须要存在happens-before关系(这两个操作可以在单个线程内,也可以在不同的线程之中)。

    as-if-serial语义

    as-if-serial语义的意思是:不管怎么重排序,(单线程)程序的执行结果不能被改变。编译器、runtime和处理器都必须遵守as-if-serial语义。

    happens-before

    1. 程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作。
    2. 锁定规则:一个unlock操作先行发生于后面对同一个锁的lock操作。
    3. volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作。
    4. 传递规则:如果操作A先行发生于操作B,操作B先行发生于操作C,那么操作A先行发生于操作C。
    5. 线程启动规则:Thread对象的start()方法先行发生于此线程的每一个动作。
    6. 线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生。
    7. 线程终结规则:线程中所有的操作都先行发生于线程的终止检测。
    8. 对象终结规则:一个对象的初始化完成先行发生于它的finalize()方法的开始。

    两个操作如果不能从happens-before中推断出执行顺序,那么就无法保证它们的有序性。

    重排序

    重排序也被分为

    1. 编译器优化的重排序:编译器在不改变单线程的程序语义的前提下,可以重新安排执行顺序。
    2. 指令级并行的重排序:如果不存在数据依赖性,处理器可以改变语句对应的机器指令都执行顺序。
    3. 内存系统的重排序:由于处理器使用缓存和缓冲区,使得加载和存储操作看上去可能是乱序执行。
      后两种重排序都属于处理器重排序,这三种重排序导致多线程中数据不可见,程序结果不符合预期。我们可以使用volatile和锁这两种方式解决重排序问题,那么从底层开始看起,底层是如何解决重拍序的问题的呢?对于编译器重排序,编译器会禁止特定类型的编译器重排序;对于处理器重排序,会插入特定类型的内存屏障,通过内存屏障禁止特定类型的处理器重排序。

    数据依赖性和控制依赖性

    上述中指令级并行的重排序提到了数据依赖性,那么数据依赖性是什么呢?除了数据依赖性还有控制依赖性,那么控制依赖性又是什么?

    数据依赖性

    如果线程A写了一个变量为1,线程B需要读取这个变量的值,线程B都读需要依赖线程A的写,那么这个操作就被称作数据依赖性。要注意,数据依赖性仅仅针对单个线程中执行的操作,多个线程之间的数据依赖性不被编译器和处理器考虑。

    控制依赖性

    如下代码

    if(flag) //1 
    a=i++; //2
    

    第二行的执行依赖于第一行的判断,所以第一行和第二行存在控制依赖性。控制依赖性会影响指令序列执行的并行度,为此编译器和处理器会采用猜测执行来克服控制相关性对并行度的影响。就像这个程序中,处理器会提前计算i++的值,并把值临时保存在一个名为重排序缓存的硬件缓存中,当第一行的判断结果为true时,会把值赋给a。在单线程程序中,控制依赖的重排序不会影响程序执行结果,但在多线程程序中,则可能会影响。

    内存屏障

    了解了以上几种概念,接下来介绍最重要也是java实现有序性的保障——内存屏障,内存屏障是一个CPU指令,确保一些特定操作的执行顺序,影响一些数据的可见性。
    内存屏障分为Load和Store

    1. Load 在读指令前插入读屏障,让告诉缓存中的数据失效,重新从主内存中加载
    2. Store 在写指令后插入写屏障,能让写入缓存的最新数据写回主内存

    又可细分为LoadLoad屏障、StoreStore屏障、LoadStore屏障以及StoreLoad屏障

    LoadLoad屏障

    序列:Load1、LoadLoad、Load2
    确保Load1所要读入的数据能够在Load2和后续Load指令访问前读入

    StoreStore屏障

    序列:Store1、StoreStore、Store2
    确保Store1写入的数据在Store2以及后续Store指令之前对其他线程可见

    LoadStore屏障

    序列:Load1、LoadStore、Store2
    确保Load1所要读入的数据在Store2和后续Store指令前读取

    StoreLoad屏障

    序列:Store1、StoreLoad、Load2
    确保Store1写入的数据在Load2和后续Load指令前对其他线程可见
    StoreLoad屏障拥有其他三种内存屏障的功能

    java中内存屏障的使用

    volatile

    写前:StoreStore
    写后:StoreLoad
    读后:LoadLoad
    读后:LoadStore
    (上述方法为保守策略,实际中编译器会根据具体情况省略掉不必要的屏障)
    简单来说,就是当第一个操作为volatile读时,不管第二个操作是什么,都不能重排序;
    就是当第二个操作为volatile写时,不管第一个操作是什么,都不能重排序;
    当第一个操作是volatile写,第二个操作是volatile读时,不能重排序。

    获取锁和释放锁

    在上文中,已经介绍了获取锁和释放锁可以保证数据的可见性,其实锁的释放和获取也是利用了volatile和cas来执行的。

    final域

    对于final域,编译器和处理器要遵循以下两个规则

    1. 在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。
    2. 初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序。

    第一条是针对final写的,编译器会在final域的写之后,构造函数返回之前写入一个StoreStore屏障,这个屏障会禁止把final域的写重排序到构造函数之外。
    第二条是针对final读的,编译器会在final域的读之前插入一个LoadLoad屏障。

    顺序一致性内存模型

    接下来顺便讲一讲顺序一致性内存模型,这个模型是一个理想化的理论模型。在设计的时候,处理器的内存模型和编程语言的内存模型都会以顺序一致性内存模型作为参考。
    顺序一致性内存模型有两大特性:

    1. 一个线程中的所有操作必须按照程序的顺序来执行。
    2. (不管程序是否正确同步)所有线程都只能看到一个单一的操作执行顺序。在顺序一致性内存模型中,每个操作都必须原子执行且立刻对所有线程可见。

    同步程序的顺序一致性效果

    JMM中,临界区内的代码可以重排序并且JMM在退出和进入临界区时会做一些特别的处理,使得线程在这两个时间点具有与顺序一致性模型相同的内存视图。

    未同步程序的执行特性

    对于未同步或未正确同步的多线程程序,JMM只保证最小安全性,即线程执行时读取到的值要么是之前线程写入的值,要么是默认值,不会凭空产生。JMM不保证未正确同步程序的执行结果与该程序在顺序一致性模型中的执行结果一致。

    顺序一致性内存模型和JMM的差异

    1. JMM不保证所有操作按照程序的顺序执行。
    2. JMM不保证所有线程能看到一致的操作执行顺序,因为JMM中不保证每个操作必须立即对任意线程可见。
    3. JMM不保证对64位变量(引用类型)的写操作具有原子性。因为在32位操作系统中,对64位变量的写操作是分为两个32位进行的,所以32位操作系统对64位变量的写不具有原子性,但是对64位变量的读操作是保证原子性的且对基本变量也是原子性的。

    参考
    《Java并发编程的艺术》

    展开全文
  • 多线程与高并发(二) volatile关键字 保证线程可见性 如果不加volatile关键字 线程A更改数据 线程B获取新数据时间不可控制 线程间不可见 即堆内存中数据是否变化 和 线程B是否读取到堆内存中数据 不可...

    多线程与高并发(二)

    volatile关键字

    保证线程可见性

    • 如果不加volatile关键字 线程A更改的数据 线程B获取新数据的时间不可控制 线程间不可见

      即堆内存中的数据是否变化 和 线程B是否读取到堆内存中的数据 不可控制

    • 加了volatile之后线程间数据可见性

      MESI CPU的缓存一致性协议

    禁止指令重排序

    • 编译器编译期间可能会发生指令重排(目的是为了更快的让CPU执行)

    • DCL单例

    • Double Check Lock

    • Mgr06.java

    • loadfence原语指令

    • storefence原语指令

    单例模式中的双重检查加锁

    • INSTANCE 单例对象上是否需要添加volatile关键字?

      需要 防止指令重排序

      INSTANCE = new Mrg06()这行代码中 注意:JVM给对象初始化时

      • 给对象申请内存
      • 给对象的成员变量初始化
      • 把内存的内容赋值给INSTANCE

      超高并发情况下 可能会出现由于指令重排导致的对象数据不准确的情况

    synchronized优化

    锁的细化

    • 锁定的代码越少越好

    锁的粗化

    • 锁的争用非常频繁时 考虑粗化锁

    锁定的对象出现变化

    • 即锁定的object被重新初始化之类的

      利用final关键字修饰对象

    CAS(无锁优化 自旋)

    Atomic原子类

    • Compare And Set

    • cas(V, Expected, NewValue)

      • if V == E

        V = New

        otherwise try again or fail

    • CPU原语支持

    • ABA问题

      • 添加版本号控制(cas中添加version的比较)

    Unsafe类

    • weakCompareAndSet(弱指针)
    展开全文
  • 缓存并发

    2019-05-10 17:14:18
    在高并发下,缓存失效会出现线程(进程)同时查询DB,同时设置缓存的情况,这可能造成 DB 压力过大,还有缓存频繁更新的问题。可以使用锁(分布式锁)来控制同时只有一个线程(进程)查询数据库,其他线程(进程...

    在高并发下,缓存失效会出现多个线程(进程)同时查询DB,同时设置缓存的情况,这可能造成 DB 压力过大,还有缓存频繁更新的问题。可以使用锁(分布式锁)来控制同时只有一个线程(进程)查询数据库,其他线程(进程)等待。

    展开全文
  • volatile介绍可见性问题让一个线程对共享变量修改,能够及时被其他线程...对volatile变量v写入,与所有其他线程后续对v读同步要避免可见性问题,volatile 需要具有哪些功能禁止缓存;volatile变量访问控制...
  • 文章目录一、结果缓存缓存类型配置二、 连接控制三、 并发控制 对于热点数据,或者对于一些长期不会变化数据来说,减少数据库访问查询,对查询结果进行缓存处理。 一、结果缓存 结果缓存 ,用于加速热门数据访问...
  • 参考: 聊聊并发(三)——JAVA线程池...控制并发线程Semaphore、ScheduledThreadPoolExcutor、BlockingQueue、ReadWriteLock 1. 线程池 1.1 为什么要使用线程池 合理利用线程池能够带来三个好处。 第一:...
  • 多线程并发库应用八-线程池

    千次阅读 2018-01-06 21:58:25
    Java通过Executors提供几种线程池,主要...newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。 newSingleThreadExecutor 创建一个单线程线程池,它只会用唯一工作线程
  • 一个共享缓存失效后,接下来有线程尝试从后台数据库服务器获取数据来更新缓存时,因为只需要一个线程完成从数据库中取数据然后在放在缓存内即可,然后其他线程再去取这个缓存,并需要并发的更新这个缓存。...
  • 一、volatile作用 保证线程可见性:堆内存是所有线程共享里面的内存,除此之外,每个线程都有...禁止指令重排序:cpu为了提升效率,执行一条指令会并发的执行,每次写都会被线程度读到,加了volatile,cpu就会按着顺序
  • 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。Java虚拟机允许程序同时运行多个执行线程。多线程:在单个程序中可以同时运行多个不同的线程,执行不同的任务...
  • 03-09 11:00:10概述为了提高并发MySQL加入了版本并发控制,它把旧版本记录保存在了共享表空间,在事务未提交之前对应行记录还是受到锁限制,当事务提交之后对应记录行就在缓存中被修改了记录也被持久化了,...
  • 12.线程并发库和线程池的作用 简单了解过,JDK5中增加了并发库,java.util.concurrent中提供了对线程优化.管理的各项操作,该包提供了线程的运行,线程池的创建,线程生命周期的控制.线程池:java.util.concurrent....
  • 多线程

    2021-02-01 03:07:50
    这里写目录标题多线程Object里关于多线程的方法以打印奇数偶数为例守护线程死锁比较经典面试题 : 生产者和消费者定时器线程池CachedThreadPool 可缓存线程池FixedThreadPool会创建一个固定长度线程池 , 可以控制...
  • MySQL逻辑架构 连接/线程管理 每个客户端都会在服务器...个查询需要在同一时刻修改数据,产生并发控制的问题 读写锁 在处理并发读或写时,可以通过实现一个由两种类型锁组成锁系统来解决问题。这两种类型
  • 2.判断对错:当程序计算的正确性取决于相对的时间或者调度器所控制的多线程交叉时,你会遇到数据竞争问题 错 当程序计算的正确性取决于相对时间或者调度器所控制的多线程交叉时,竞态条件就会发生 3.给出同步的定义 ...
  • 为了提高并发MySQL加入了版本并发控制,它把旧版本记录保存在了共享表空间,在事务未提交之前对应行记录还是受到锁限制,当事务提交之后对应记录行就在缓存中被修改了记录也被持久化了,当刷新线程按一定...
  • Java多线程使用

    千次阅读 2017-10-20 15:44:43
    Java多线程使用 Java通过Executors提供四种线程池,分别为: newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 newFixedThreadPool ...
  • 多线程的实现原理

    2019-02-13 18:27:29
     在Java中提供了一系列和并发处理相关关键字,比如volatile、Synchronized、final、juc等,这些就是Java内存模型封装了底层实现后提供给开发人员使用关键字,在开发多线程代码时候,我们可以直接使用 ...
  • 概述为了提高并发MySQL加入了版本并发控制,它把旧版本记录保存在了共享表空间,在事务未提交之前对应行记录还是受到锁限制,当事务提交之后对应记录行就在缓存中被修改了记录也被持久化了,当刷新线程按...
  • 上一节我们知道了java如何创建线程并启动,当...竞态条件:当计算的正确性取决于相对时间或者调度器所控制的多线程交叉时,竞态条件就会发生。如下例子:if(a==10.0){b=a/2.0;}假如一条线程已经执行完了if(a == 10....
  • newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。 newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。 newSingleThreadExecutor 创建一个单线程化...
  • 其实java的多线程并发问题最终都会反映在java的内存模型上,所谓线程安全无非是要控制多个线程对某个资源的有序访问或修改。总结java的内存模型,要解决两个主要的问题:可见性和有序性。我们都知道计算机有高速缓存...
  • 其实java的多线程并发问题最终都会反映在java的内存模型上,所谓线程安全无非是要控制多个线程对某个资源的有序访问或修改。总结java的内存模型,要解决两个主要的问题:可见性和有序性。我们都知道计算机有高速缓存...
  • 多线程编程

    2016-06-01 22:34:45
    其实java的多线程并发问题最终都会反映在java的内存模型上,所谓线程安全无 非是要控制多个线程对某个资源的有序访问或修改。总结java的内存模型,要解决两个主要的问题:可见性和有序性。我们都知道计算机有高速...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 388
精华内容 155
关键字:

缓存的多线程并发的控制