什么是并发编程?

首先我们要知道什么是并发? 什么是并行?

并行: 多件事情在同一时刻同时发生

并发: 在同一时间内,多个事情交替执行

并发编程: 比如抢票,秒杀等在同一场景下,有大量的请求访问同一资源, 会出现一些安全性的问题,所以要通过编程来控制多个线程依次访问资源,称为并发编程

引发并发编程的根本原因

因为所有的java代码都是在java虚拟机中运行的, 而java虚拟机也有自己的模型-----

Java 内存模型(Java Memory Model,JMM)

Java 内存模型中规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量,在工作内存中操作完之后,再将数据写入主内存中.

这里的工作内存是 JMM 的一个抽象概念,也叫本地内存,其存储了该线程读 /写共享变量的副本, 就像每个处理器内核拥有私有的高速缓存,JMM 中每个线程拥有私有的本地内存。

并发编程的三个核心问题(不可见性,乱序性,非原子性)

1.不可见性

多个线程同时对共享资源进行操作, 彼此之间是不可见的,操作完之后再写入到主内存中,这样可能会出现问题,与我们预期的结果不相符

例如:

我们预期的结果应该是count=2;

然而这是不一定的, 当线程1从主内存中拿到count进行++操作后变成了1,还没来得及写入主内存时,线程2也拿到了count=0的数据进行了++操作是的其变成了1,两个线程写入主内存后最终的结果count是为1的,而不是2,也就是线程1操作了主内存中的数据,线程2并不知道.

2.无序性

为了优化性能,对一些代码指令进行了顺序重排,提高程序的速度

3.原子性

原子的意思代表着——“不可分”, 原子性是拒绝多线程交叉操作的,不论是多核还是单核,具有原子性的量,同一时刻只能有一个线程来对它进行操作, 线程切换导致了原子性问题

CPU 能保证的原子操作是 CPU 指令级别的(i++,就只有一条语句,进行一次操作),而不是高级语言的操作符(i++,有三个操作过程), 高级语言里一条语句往往需要多条 CPU 指令完成。如 count++,至少需要三条 CPU指令。

指令 1:首先,需要把变量 count 从内存加载到工作内存;

指令 2:之后,在工作内存执行 +1 操作;

指令 3:最后,将结果写入内存;

如何解决这三个问题?

1.使用volitile关键字

一旦一个共享变量(类的成员变量、类的静态成员变量)被 volatile 修饰之后:

1. 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。

2. 禁止进行指令重排序。

3. volatile 不能保证对变量操作的原子性。

volitile的底层实现原理

使用 Memory Barrier(内存屏障)。

内存屏障是一条指令,该指令可以对编译器(软件)和处理器(硬件)的指令重排做出一定的限制,比如,一条内存屏障指令可以禁止编译器和处理器将其后面的指令移到内存屏障指令之前。

Volatile 变量编译为汇编指令会多出Lock 前缀.

有序性实现:主要通过对 volatile 修饰的变量的读写操作前后加上各种特定的内存屏障来禁止指令重排序来保障有序性的。

可见性实现:主要是通过 Lock 前缀指令 + MESI 缓存一致性协议来实现的。对 volatiile 修饰的变量执行写操作时,JVM 会发送一个 Lock 前缀指令给CPU,CPU 在执行完写操作后,会立即将新值刷新到存,同时因为 MESI 缓存一致性协议,其他各个 CPU 都会对总线嗅探,看自己本地缓存中的数据是否被别人修改,如果发现修改了,会把自己本地缓存的数据过期掉。然后这个 CPU里的线程在读取改变量时,就会从主内存里加载最新的值了,这样就保证了可见性

可见性实现的例子:

public class ThreadDemo implements  Runnable{/*volatile 修饰的变量,在一个线程中被修改后,对其它线程立即可见禁止cpu对指令重排序*/private    boolean  flag = false;//共享数据public void run() {try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}this.flag = true;//让一个线程修改共享变量值System.out.println(this.flag);}public boolean getFlag() {return flag;}public void setFlag(boolean flag) {this.flag = flag;}
}
public class TestVolatile {public static void main(String[] args) {//创建线程任务ThreadDemo td = new  ThreadDemo();Thread t = new Thread(td);//创建线程t.start();//main线程中也需要使用flag变量while(true){if(td.getFlag()){//false-trueSystem.out.println("main---------------");break;}}}
}

上面例子中,main主线程中执行一个子线程,还要执行一个while循环,只有当子线程中的flag为true时才可以跳出while循环

当共享数据flag没有被volitile修饰时,输出结果如下:

一直在死循环,没有跳出while循环,但是在子线程中flag已经改为了true,也已经输出了,说明main主线程和子线程彼此的操作时不可见性的, 但是有volitile修饰时,就会跳出循环.

有序性实现的例子:

/*模拟指令重排序*/
public class Reorder {private  volatile static int x;private  volatile static int y;private  volatile static int a;private  volatile static int b;public static void main(String[] args) throws InterruptedException {int i = 0;for(;;) {i++;x = 0; y = 0;a = 0; b = 0;Thread one = new Thread(new Runnable() {public void run() {a = 1;x = b;}});Thread other = new Thread(new Runnable() {public void run() {b = 1;y = a;}});one.start();other.start();one.join();other.join();String result = "第" + i + "次 (" + x + "," + y + ")";if(x == 0 && y == 0) {System.err.println(result);break;} else {System.out.println(result);}}}}

有volitile修饰时输出结果如下:

没有volitile修饰时:

可以看到,有volitile修饰时,代码的输出是有序的.

2.通过加锁的方式,让线程互斥执行来保证一次只有一个线程对共享资源访问,从而保证原子性.

synchronized : 关键字 修饰代码块,方法 自动获取锁,自动释放锁

ReentrantLock : 类 只能对某段代码修饰 需要手动加锁,手动释放锁.

3.在java中还提供一些原子类,在低并发情况下使用,是一种无锁实现.

原子类的原子性是通过 volatile + CAS 实现原子操作的。

AtomicInteger 类中的 value 是有 volatile 关键字修饰的,这就保证了 value的内存可见性,这为后续的 CAS 实现提供了基础。低并发情况下:使用 AtomicInteger。

4.采用CAS机制(CAS(Compare-And-Swap) 比较并交换)从而保证原子性

CAS是实现乐观锁的一种方式,是一种自旋式思想,是一种轻量级的锁机制

CAS 包含了三个操作数:

①内存值 V

②预估值 A (比较时,从内存中再次读到的值)

③更新值 B (更新后的值)

当且仅当预期值 A==V,将内存值 V=B,否则什么都不做。

这种做法的效率高于加锁,当判断不成功不能更新值时,不会阻塞,继续获得 cpu执行权,继续判断执行。

自旋思想:

第一次从主内存中获取值到工作内存中,存储作为预期值,然后将对象数据修改,将工作内存中值写入到主内存, 在写入之前需要做一个判断.用预期值与主内存中的值进行比较,如果预期值与主内存中值一致,说明没有其他线程修改, 将更新数的值,写入到主内存,如果预期值与主内存中值不一致, 说明其他线程修改了主内存的值, 这时就需要重复操作整个过程

特点:

不加锁,所有的线程都可以对共享数据操作, 适合地并发是使用,由于不加锁,其他线程不需要阻塞,效率高,但是在大并发时,不停自旋判断,导致cpu占用率高,容易导致 CPU 跑满.

ABA 问题

当某个线程将内存值由 A 改为了 B,再由 B 改为了 A。当另外一个线程使用预期值去判断时,预期值与内存值相同,当前线程的 CAS 操作无法分辨当前 V 值是否发生过变化。

如何解决ABA问题?

解决 ABA 问题的主要方式,通过使用类添加版本号,来避免 ABA 问题。如原先的内存值为(A,1),线程将(A,1)修改为了(B,2),再由(B,2)修改为(A,3)。此时另一个线程使用预期值(A,1)与内存值(A,3)进行比较,只需要比较版本号 1 和 3,即可发现该内存中的数据被更新过了

java并发编程(并发编程的三个问题)相关推荐

  1. Java JUC高并发编程(三)-CallableJUC辅助类

    目录 一.Callable接口 二.JUC辅助类 1.减少计数CountDownLatch 2.循环栅栏CyclicBarrier 3.信号灯Semaphore 一.Callable接口 Callab ...

  2. Java并发编程实战_阿里P9整理分享的亿级流量Java高并发与网络编程实战PDF

    前言 为了帮助初级开发者快速掌握高并发.网络编程.微服务.海量数据的处理这些实用技术,本文以"理论+范例"的形式对各个知识点进行了详细的讲解,力争让读者在实践中快速掌握相关知识. ...

  3. Java增强之并发编程

    Java增强之并发编程 1 多线程 1.1 进程及线程 程序启动的时候,电脑会把这个程序加载到内存,在内存中需要给当前的程序分配一段的独立运行的空间,这个空间就专门负责这个程序的运行.每个应用程序运行 ...

  4. Java零基础并发编程入门

    Java零基础并发编程入门 并发编程主要包括: 线程,同步,future,锁,fork/join, volatile,信号量,cas(原子性,可见性,顺序一致性),临界性,分布式 了解基础: JMM: ...

  5. java书籍_还搞不定Java多线程和并发编程面试题?你可能需要这一份书单!

    点击蓝色"程序员书单"关注我哟 加个"星标",每天带你读好书! ​ 在介绍本书单之前,我想先问一下各位读者,你们之前对于Java并发编程的了解有多少呢.经过了1 ...

  6. 《Java并发编程的艺术》——Java中的并发工具类、线程池、Execute框架(笔记)

    文章目录 八.Java中的并发工具类 8.1 等待多线程完成的CountDownLatch 8.2 同步屏障CyclicBarrier 8.2.1 CyclicBarrier简介 8.2.2 Cycl ...

  7. 《Java并发编程的艺术》读后笔记-Java中的并发工具类(第八章)

    文章目录 <Java并发编程的艺术>读后笔记-Java中的并发工具类(第八章) 1.等待多线程完成的CountDownLatch 2.同步屏障CyclicBarrier 2.1 Cycli ...

  8. Java多线程与并发编程终极宝典

    阅读本文需要了解的概念 原语 所谓原语,一般是指由若干条指令组成的程序段,用来实现某个特定功能,在执行过程中不可被中断.在操作系统中,某些被进程调用的操作,如队列操作.对信号量的操作.检查启动外设操作 ...

  9. 阿里P9整理分享的亿级流量Java高并发与网络编程实战PDF

    前言 有人调侃我们说: 程序员不如送外卖.送外卖是搬运食物,自己是搬运代码,都不产出新的东西-- 透支体力,又消耗健康,可替代性极强,30岁之后就要面临被优化的危险-- 想跳槽,但是更高的平台难进,同 ...

  10. 基于《狂神说Java》JUC并发编程--学习笔记

    前言: 本笔记仅做学习与复习使用,不存在刻意抄袭. -------------------------------------------------------------------------- ...

最新文章

  1. 李彦宏:从没觉得百度模仿谷歌;马化腾:做ICO数字货币有很多风险
  2. 限制php解析、user_agent、php相关配置
  3. Linux通过XAMPP集成软件包搭建LAMPP环境
  4. mysql的字符集编码_MySQL的字符编码设置
  5. Linux启动/停止/重启Mysql数据库的方法
  6. UVA - 101:The Blocks Problem
  7. 同步,异步,多线程,你怎么看?
  8. 容器编排技术  - Kubernetes kubectl convert 命令详解
  9. Promises 对比 callbacks
  10. 股票决定卖不卖?近六成粉丝支持马斯克出售10%特斯拉股票
  11. 【高并发】java中的CAS,你需要知道的东西
  12. 几种无线通讯介绍与比较(蓝牙,WiFi,IrDA,NFC,Zigbee,UWB)
  13. css中调整高度充满_css - DIV高度怎样充满容器?
  14. html中表格table的内容居中显示
  15. lcm模块 oracle,lcm模组有哪些配件组成
  16. python自动填表程序_Python的PAMIE IE自动化测试填表提交工具
  17. amr转换成mp3 java_微信开发-将amr格式转换为mp3格式
  18. 511遇见易语言文本处理寻找文本
  19. (第二章)HTML基本标记
  20. 网站流量日志数据分析系统(模块开发----数据仓库设计)

热门文章

  1. sqlyog安装和破解
  2. 用c语言switch计算奖金,求助。。关于用switch编写简易计算器
  3. 【2020年高被引学者】 王威廉 加州大学圣塔芭芭拉分校
  4. Vulkan的基本概念:如何使用Vulkan绘制三角形?
  5. 渗透之——使用Metasploit实现对缓冲区栈的溢出攻击
  6. 红米联通版刷机包 MIUIV6 扁平化 精简 稳定 流畅 省电 长用版
  7. 光电耦合器原理及应用介绍
  8. 计算机usb口不识别读卡器,windows7系统下usb读卡器读不出来如何解决
  9. 交通运输词汇(zt)
  10. 短线必看庄家克星底顶侦探 同花顺发现大庄家幅图公式