精华内容
下载资源
问答
  • 并发中的三大特性详解

    千次阅读 2020-05-21 22:57:04
    前言:Java并发编程的三大特性:原子性、可见性、有序性。要保证并发代码的安全性则必须满足这三大特性 原子性:一个或者多个操作,要么全部执行(执行的过程是不会被打断的)、要么全部不执行。 案例分析:...

    前言:Java并发编程的三大特性:原子性、可见性、有序性。要保证并发代码的安全性则必须满足这三大特性

    原子性的定义:一个或者多个操作,要么全部执行(执行的过程是不会被打断的)、要么全部不执行。

    原子性案例分析1:复合操作如 i++ http://www.51testing.com/html/87/300987-814461.html 

    原子性案例分析2:A、B同时给C转账。

    比如A和B同时向C转账10万元。如果转账操作不具有原子性,A在向C转账时,读取了C的余额为20万,然后加上转账的10万,计算出此时应该有30万,但还未来及将30万写回C的账户,此时B的转账请求过来了,B发现C的余额为20万,然后将其加10万并写回。然后A的转账操作继续——将30万写回C的余额。这种情况下C的最终余额为30万,而非预期的40万。 如果A和B两个转账操作是在不同的线程中执行,而C的账户就是你要操作的共享变量,那么不保证执行操作原子性的后果是十分严重的。
     

    原子性的解决:内置锁(同步关键字):synchronized;显示锁:Lock;自旋锁:CAS;

     

    有序性:

    处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。如下:

    int a = 10;    //语句1
    int r = 2;    //语句2
    a = a + 3;    //语句3
    r = a*a;     //语句4
    则因为重排序,他还可能执行顺序为 2-1-3-4,1-3-2-4
    但绝不可能 2-1-4-3,因为这打破了依赖关系。
    显然重排序对单线程运行是不会有任何问题,而多线程就不一定了,所以我们在多线程编程时就得考虑这个问题了。

    as-if-serial 语义的意思指:不管怎么重排序(编译器和处理器为了提高并行度),(单线程)程序的执行结果不能被改变。编译器,runtime 和处理器都必须遵守as-if-serial语义
    为了遵守as-if-serial语义,编译器和处理器不会对存在数据依赖关系的操作做重排序,因为这种重排序会改变执行结果。但是,如果操作之间不存在数据依赖关系,这些操作可能被编译器和处理器重排序。为了具体说明,请看下面计算圆面积的代码示例:

    double pi  = 3.14;    //A
    double r   = 1.0;     //B
    double area = pi * r * r; //C

    上面三个操作的数据依赖关系如下图所示:

    如上图所示,A和C之间存在数据依赖关系,同时B和C之间也存在数据依赖关系。因此在最终执行的指令序列中,C不能被重排序到A和B的前面(C排到A和B的前面,程序的结果将会被改变)。但A和B之间没有数据依赖关系,编译器和处理器可以重排序A和B之间的执行顺序。下图是该程序的两种执行顺序:

    as-if-serial语义把单线程程序保护了起来,遵守as-if-serial语义的编译器,runtime 和处理器共同为编写单线程程序的程序员创建了一个幻觉:单线程程序是按程序的顺序来执行的。as-if-serial语义使单线程程序员无需担心重排序会干扰他们,也无需担心内存可见性问题

    5.4.重排序的意义
    为什么会重排序,上面这几个阶段里大概提到了,提高并行效率编译器认为重排序后执行更优,指令级重排序并行效率更好等等。在单个线程的视角看来,重排序不存在任何问题,重排序不改变执行结果,如下例:

    int a = 1;
    int b = 2;
    int c = a + b;

    c因为对a和b有数据依赖,因此c不会被重排序,但是a 、b的执行可能被重排序。但在单个线程下,这种重排序不存在任何问题,不论先初始化a、还是先初始化b,c的值都是3。但是在多线程情况下,重排序就可能带来问题,如下例:
    线程T1执行:

    a = 1; //共享变量 int a
    b = true; //共享变量 boolean b

    线程T2执行:

    if (b){
        int c = a;
        System.out.println(c);
    }

    假如某个并发时刻,T2检测到b变量已经是true值了,并且变量都对T2可见。c 赋值得到的一定是1吗?

    答案是不一定,原因就是重排序问题的存在,在多线程环境下,会造成问题。T1线程如果 a 和 b变量的赋值被重排序了,b先于 a发生,这个重排序对T1线程本身不存在什么问题,之前我们已经讨论过。但是在T2这个线程看来,这个执行就有问题了,因为在T2看来,如果没有重排序,b值变为true之前,a已经被赋值1了。而重排序使得这个推断变得不确定,b有可能先执行,a还没来的及执行,此时线程T2已经看到b变更,然后去获取a的值,自然不等于1。

    5.5.happen-before原则
    因为有以上重排序问题,会导致并发执行的问题,那么有没有方法解决呢?

    happen-before原则,就是用来解决这个问题的一个原则说明,它告诉我们的开发者,你放心的写并发代码,但是你要遵循我告诉你的原则,你就能避免以上重排序导致的问题。

    这个原则是什么呢?

    1.程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作;
    2.锁定规则:一个unLock操作先行发生于后面对同一个锁额lock操作;
    3.volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作;
    4.传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C;
    5.线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作;
    6.线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生;
    7.线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行;
    8.对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始;

    第一条

    单线程情况下,happen-before原则告诉你,你放心的认为代码写在前面的就是先执行就ok了,不会有任何问题的。(当然实际并非如此,因为有指令重排序嘛,有的虽然写在前面,但是未必先执行,但是单线程情况下,这并不会给实际造成任何问题,写在前面的代码造成的影响一定对后面的代码可见)

    happen-before 有一种记法,hb(a,b) ,表示a比b先行发生。单线程情况下,写在前面的代码都比写在后面的代码先行发生

    int a = 1;
    int b= 2;

    hb(a,b)
    第二条
    看如下代码
    线程T1:

    a = 1;
    lock.unlock();
    b = ture;

    线程T2:

    if (b){
        lock.lock();
        int c = a;
        System.out.println(c);
        lock.unlock();
    }

    此前在讲重排序的时候说过这个问题,说c有可能读取到的a值不一定是1。因为重排序,导致a的赋值语句可能没执行。但是现在在

    b赋值之前加了解锁操作,线程T2在读取到b值变更后,做了加锁操作。这时候就是第二条原则生效的时候,它告诉我们,假如在时间上T1的lock.unlock()先执行了,T2 的lock.lock()后执行,那么T1 unlock之前的所有变更,a = 1这个变更,T2是一定可见的,即T2 在 lock后,c拿到的值一定是 a 被赋值1的值。

    因为 a = 1 和 lock.unlock() 有 hb 关系 hb(a=1 , lock.unlock() )

    第二条原则 hb(unlock, lock), 而 hb(lock , c = a ),因此c在被赋值a时,a=1一定会先行发生。

    第三条
    volatile关键字修饰的变量的写先行发生与对这个变量的读,如下
    线程T1:

    a = 1;
    vv = 33;//volatile
    b = ture;

    线程T2:

    if (b){
        int ff = vv;// vv is volatile
        int c = a;
        System.out.println(c);
    }

    与前面的锁原则一样,这次是volatile变量 写happen-before读。线程T2在读取a变量前先读取以下vv这个volatile变量。因为第三条原则的存在,只要T1在时间上执行了vv写操作,T2在执行vv读操作后,a=1的赋值一定可以被T2读到。

    第四条、第五条、第六条

    线程T1 start方法,先行发生于T1即将做的所有操作。
    如,在某个线程中启动thread1

    a = 1;
    thread1.start();

    如上,a =1 先行发生 thread.start(),而第四条规则又说,start方法先行发生该线程即将做的所有操作,那么a =1 ,也必将先行发生于 thread1 的任何操作。所以thread1启动后,是一定可以读取到a的值为1的

    五、六条类似,线程终止前的所有操作先行发生于终止方法的返回。这就保障了一个线程结束后,其他线程一定能感知到线程所做的所有变更。

    第七条
    对象被垃圾回收调用finalize时,对象的构造一定已经先行发生。

    第八条
    传递性

    可见性的定义:一个线程对共享变量的写入时,能对另一个线程可见。理解可见性必须对JMM模型有深刻理解。如图:

    å¨è¿éæå¥å¾çæè¿°

    共享内存模型指的就是Java内存模型(简称JMM),JMM决定一个线程对共享变量的写入时,能对另一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在
    从上图来看,线程A与线程B之间如要通信的话,必须要经历下面2个步骤:

    1. 首先,线程A把本地内存A中更新过的共享变量刷新到主内存中去。
    2. 然后,线程B到主内存中去读取线程A之前已更新过的共享变量。 

    当然,我们可以用synchronized来保证这个过程。但是Java1.5以后提供了更加轻量的锁volatile

    Volatile与Synchronized区别
    (1)从而我们可以看出volatile虽然具有可见性但是并不能保证原子性。
    (2)性能方面,synchronized关键字是防止多个线程同时执行一段代码,就会影响程序执行效率,而volatile关键字在某些情况下性能要优于synchronized。但是要注意volatile关键字是无法替代synchronized关键字的,因为volatile关键字无法保证操作的原子性。

     

    可见性案例分析:https://blog.csdn.net/BruceLiu_code/article/details/104431990/

    可见性的解决:最轻量的就是:Volatile

    展开全文
  • 并发编程三大特性

    2019-10-08 11:41:43
    3、三大特性 1)可见性 可见性是指当一个线程修改了共享变量后,其他线程能够立即看见这个修改。 2)原子性 原子性是指一个操作是不可中断的,要么全部执行成功要么全部执行失败。 3)有...

    1、定义

    所谓并发编程是指在一台处理器上“同时”处理多个任务。并发是在同一实体上的多个事件。多个事件在同一时间间隔发生。

    2、目标

    并发编程的目标是充分的利用处理器的每一个核,以达到最高的处理性能。

    3、三大特性

    1)可见性

    可见性是指当一个线程修改了共享变量后,其他线程能够立即看见这个修改。

    2)原子性

    原子性是指一个操作是不可中断的,要么全部执行成功要么全部执行失败。

    3)有序性

    有序性是指程序指令按照预期的顺序执行而非乱序执行,乱序又分为编译器乱序和CPU执行乱序。

    展开全文
  • 从JDK源码看Java并发特性

    千次阅读 2017-02-19 20:58:12
    索引下前面写的篇关于从JDK源码看Java并发特性的文章 文章列表 从JDK源码角度看java并发的原子性如何保证 从JDK源码角度看java并发的公平性 从JDK源码角度看java并发线程的中断 从JDK源码角度看并发竞争的超时 ...

    索引下前面写的篇关于从JDK源码看Java并发特性的文章


    文章列表
    从JDK源码角度看java并发的原子性如何保证
    从JDK源码角度看java并发的公平性
    从JDK源码角度看java并发线程的中断
    从JDK源码角度看并发竞争的超时
    从JDK源码角度看并发锁的优化
    从JDK源码角度看线程的阻塞和唤醒
    从JDK源码角度看线程池原理

    ========广告时间========

    公众号的菜单已分为“分布式”、“机器学习”、“深度学习”、“NLP”、“Java深度”、“Java并发核心”、“JDK源码”、“Tomcat内核”等,可能有一款适合你的胃口。

    鄙人的新书《Tomcat内核设计剖析》已经在京东销售了,有需要的朋友可以购买。感谢各位朋友。

    为什么写《Tomcat内核设计剖析》

    =========================

    展开全文
  • 在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。(例如:重排的时候某些赋值会被提前) 在Java里面,可以通过volatile...

    一.原子性是指一个操作是不可中断的。即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其它线程干扰。
    Java中的原子操作包括:
    1)除long和double之外的基本类型的赋值操作
    2)所有引用reference的赋值操作
    3)java.concurrent.Atomic.* 包中所有类的一切操作。
    但是java对long和double的赋值操作是非原子操作!!long和double占用的字节数都是8,也就是64bits。在32位操作系统上对64位的数据的读写要分两步完成,每一步取32位数据。这样对double和long的赋值操作就会有问题:如果有两个线程同时写一个变量内存,一个进程写低32位,而另一个写高32位,这样将导致获取的64位数据是失效的数据。因此需要使用volatile关键字来防止此类现象。volatile本身不保证获取和设置操作的原子性,仅仅保持修改的可见性。但是java的内存模型保证声明为volatile的long和double变量的get和set操作是原子的。

    public class UnatomicLong implements Runnable {
        private static long test = 0;
    
        private final long val;
    
        public UnatomicLong(long val) {
            this.val = val;
        }
    
        @Override
        public void run() {
            while (!Thread.interrupted()) {
                test = val; //两个线程都试图将自己的私有变量val赋值给类私有静态变量test
            }
        }
    
        public static void main(String[] args) {
            Thread t1 = new Thread(new UnatomicLong(-1));
            Thread t2 = new Thread(new UnatomicLong(0));
    
            System.out.println(Long.toBinaryString(-1));
            System.out.println(pad(Long.toBinaryString(0), 64));
    
            t1.start();
            t2.start();
    
            long val;
            while ((val = test) == -1 || val == 0) { 
           //如果静态成员test的值是-1或0,说明两个线程操作没有交叉
            }
    
            System.out.println(pad(Long.toBinaryString(val), 64));
            System.out.println(val);
    
            t1.interrupt();
            t2.interrupt();
        }
    
        // prepend 0s to the string to make it the target length
        private static String pad(String s, int targetLength) {
            int n = targetLength - s.length();
            for (int x = 0; x < n; x++) {
                s = "0" + s;
            }
            return s;
        }
    }
    

    运行发现程序在while循环时进入了死循环,这是因为使用的JVM是64bits。在64位JVM中double和long的赋值操作是原子操作。
    在eclipse中修改jre为一个32bit的JVM地址,则会有如下运行结果:
    1111111111111111111111111111111111111111111111111111111111111111
    0000000000000000000000000000000000000000000000000000000000000000
    0000000000000000000000000000000011111111111111111111111111111111
    //很明显test的值被破坏了
    4294967295

    二.有序性
    有序性即程序执行的顺序按照代码的先后顺序执行。
    在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。(例如:重排的时候某些赋值会被提前)
    在Java里面,可以通过volatile关键字来保证一定的“有序性”(具体原理在下一节讲述)。另外可以通过synchronized和Lock来保证有序性,很显然,synchronized和Lock保证每个时刻是有一个线程执行同步代码,相当于是让线程顺序执行同步代码,自然就保证了有序性

    三.可见性
    可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
    可见性,Java提供了volatile关键字来保证可见性。
    当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。
    而普通的共享变量不能保证可见性,因为普通共享变量被修改之后,什么时候被写入主存是不确定的,当其他线程去读取时,此时内存中可能还是原来的旧值,因此无法保证可见性。
    另外,通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性。

    转自: http://longload.iteye.com/blog/2307621

    展开全文
  • 项目中经常会用到自增id,比如uid,下面为大家介绍下利用mysql事务特性实现并发安全的自增ID,感兴趣的朋友可以参考下
  • Java编程:并发编程的3个特性

    千次阅读 2016-06-15 17:19:41
    并发编程中我们经常会遇到三个问题:原子性问题、可见性问题、有序性问题,下面为大家剖析一下这三个问题。如有不正之处,欢迎批评指正。1、原子性 原子行:即一个或者多个操作作为一个整体,要么全部执行,要么...
  • 并发编程之深入理解JMM&并发三大特性
  • 并发三大特性——可见性

    千次阅读 2021-10-28 22:14:12
    熟悉并发的童鞋们都知道,并发编程有三大特性,分别是可见性、有序性、原子性,今天我们从一个demo中分析可见性,以及我们如何保障可见性。 JMM模型 在我们分析可见性之前,我们需要了解一个概念,就是JMM模型,也...
  • 全套的汪文君java高并发及java8新特性教程,无加密,资源直接下载。
  • 主要介绍了全面解析Hibernate关联操作、查询操作、高级特性并发处理机制的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
  • 包含java三大高级特性的文档,《Java Reflection in Action》、《JAVA并发编程实战》、《JVM调优总结》、《深入理解Java虚拟机JVM高级特性与最佳实践》、《concurrent programming in java》,适合想深入java技术的...
  • JDK1.8的并发特性

    千次阅读 2017-03-23 15:03:38
    JDK1.8中有一些并发的新特性,可以提高变成的效率。本文写的主要是LongAdder和stampedlock的特性。 多线程发生死锁时dump查看方式: 使用命令jps:如下所示 通过这个命令我们可以得到死锁号,然后再通过命令...
  • Java知识体系最强总结(2021版)

    万次阅读 多人点赞 2019-12-18 10:09:56
    基础知识 Java概述 基础语法 面向对象 集合框架 IO流 网络编程 常用API 日期时间API 常用工具类库 单元测试 异常 日志 Java8新特性 工具 IDEA Eclipse & STS Maven Docker Git GitLab GitKraken Navicat 并发编程 ...
  • 本章主要学习Java并发中的三个特性:原子性、可见性和有序性。 在Java并发编程中,如果要保证代码的安全性,则必须保证代码的原子性、可见性和有序性。 本章的很多概念可以参考:Java并发11:Java内存模型、指令...
  • jdk1.5并发特性.

    2012-10-10 01:15:54
    关于jdk1.5新特性的书,值得拥有,不错的资料
  • 主要介绍了使用java的HttpClient实现多线程并发的相关资料,需要的朋友可以参考下
  • 在 Java并发12:并发特性-原子性、可见性和有序性概述及问题示例中,对并发中的三个特性(原子性、可见性和有序性)进行了初步学习。 本章主要就Java中保障有序性的技术进行更加全面的学习。 1.整体回顾 有序性...
  • 3.Redis使用多路复用技术,可以处理并发的连接。非阻塞IO 内部实现采用epoll,采用了epoll+自己实现的简单的事件框架。epoll中的读、写、关闭、连接都转化成了事件,然后利用epoll的多路复用特性,绝不在io上浪费...
  • 在 Java并发12:并发特性-原子性、可见性和有序性概述及问题示例中,对并发中的三个特性(原子性、可见性和有序性)进行了初步学习。 本章主要就Java中保障原子性的技术进行更加全面的学习。 1.整体回顾 原子性...
  • 由于之前看的容易忘记,因此特记录下来,以便学习总结与更好理解,该系列博文也是第一次记录...一、内容提要 多线程调试的方法 线程dump及分析 JDK8对并发的新支持 – LongAdder – CompletableFuture – ...
  • 在 Java并发12:并发特性-原子性、可见性和有序性概述及问题示例中,对并发中的三个特性(原子性、可见性和有序性)进行了初步学习。 本章主要就Java中保障可见性的技术进行更加全面的学习。 1.整体回顾 可见性...
  • 在进行高并发性能调优的时候发现了如下的一个问题: 1. 在一个事务中同时包括了SELECT,UPDATE语句 2. SELECT和UPDATE涉及到的数据为同一张表中的同一记录 3. 在并发为10的情况下就会触发数据库锁等待和死锁的情况 ...
  • 事务特性并发带来的问题

    千次阅读 2016-12-19 13:02:38
    事务是并发控制的单位,一系列操作组成的工作单元,该工作单元内的操作是不可分割的,也就是事务具有原子性,一个事务中的一系列的操作要么全部成功,要么一个都不做,所有操作必须成功完成,否则在每个操作中所作的...
  • 本文来自于csdn,本文主要从分布式的原因,事务特性,和解决方案中深入理解了分布式事务,希望对您的学习有所帮助。 分布式事务就是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的...
  • 在系统开发过程中,经常遇到数据重复插入、重复更新、消息重发发送等等问题,因为应用系统的复杂逻辑以及网络交互存在的不确定性,会导致这一重复现象,但是有些逻辑是需要有幂等特性的,否则造成的后果会比较严重,...
  • ACID 原子性(Atomic):事务包含的所有操作,要么全做,要么全不做回滚; 一致性(Consistency):从一个一致状态到另一个一致状态;eg:A、B之间转账,两者的金额总和转账前后...事务并发引起的问题以及如何...
  • 本课程包含了socket网络编程常用的所有特性,包括tcp、udp协议的基础编程,广播包,超时连接,多路复用,高并发的epoll多路复用,多线程的服务端。课程讲解理论与实践相结合,实践以代码延时讲解为主。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 365,282
精华内容 146,112
关键字:

并发特性