IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> java并发三大特性 -> 正文阅读

[Java知识库]java并发三大特性

在Java内存模型中,有三大性质:原子性、有序性和可见性

原子性

熟悉数据库特性都知道数据库sql执行中也有原子性,数据库中的原子性是这样定义的在一个事务中要么所有的sql都执行,要么都不执行。

java内存模型中的原子性也是类似,要么所有的指令都执行,要么都不执行。这样才能保证并发操作的安全性和一致性。但是并发在带来方便的同时,却不能很好的解决原子性的问题。

操作系统为了提高并行处理问题的能力,会将时间分成一个个小的分片,例如50毫秒,过了50毫秒操作系统就会重新选择一个进程来执行(称为“任务切换”)

线程切换 针对同一个cpu,线程A获取到cpu的使用权后开始执行自己的任务,但是当时间超过一个时间分片之后需要将cpu的使用权让出来,此时线程B会占有cpu的使用权。但是如果此时线程A的任务没有执行完成,又进行了线程切换,此时线程A的操作就无法保证原子性了。下面举一个例子 针对代码 count+=1;至少需要三条 CPU 指令.

(1) 将count的值从内存中读取到寄存器中。

(2) 在寄存器中进行+1操作。

(3) 将结果写入内存中。

但是当上述三个过程中线程A 刚执行完步骤(1)后,进行了线程切换,线程B重新执行上述操作,最后内存中存储的数据是1而不是2。具体流程如图2所示。

图2
以上便是线程切换带来的Java并发原子性问题。

可见性

一个线程对共享变量的修改,另外一个线程能够立刻看到,我们称为可见性。

每颗 CPU 都有自己的缓存,这时 CPU 缓存与内存的数据一致性就没那么容易解决了,当多个线程在不同的 CPU 上执行时,这些线程操作的是不同的 CPU 缓存。比如下图中,线程 A 操作的是 CPU-1 上的缓存,而线程 B 操作的是 CPU-2 上的缓存,很明显,这个时候线程 A 对变量 V 的操作对于线程 B 而言就不具备可见性了。这个就属于硬件程序员给软件程序员挖的“坑”。
在这里插入图片描述举例:

public class Test {
  private int count = 0;
  private void add() {
    int idx = 0;
    while(idx++ < 1000) {
      count += 1;
    }
  }
  public static long calc() {
    final Test test = new Test();
    // 创建两个线程,执行add()操作
    Thread th1 = new Thread(()->{
      test.add();
    });
    Thread th2 = new Thread(()->{
      test.add();
    });
    // 启动两个线程
    th1.start();
    th2.start();
    // 等待两个线程执行结束
    th1.join();
    th2.join();
    return count;
  }
}

启动了2个线程分别对一个从0开始的数字分别加1000次,最后返回的结果一定是介于1000–2000中间的数字。出现这个问题的原因是多线程操作时一个线程修改了内存中的数值时没有来得及通知其他线程所在cpu的缓存中。导致另外一个线程从缓存中读到的数据还是原来的数据,所以最后的结果达不到2000。这也就是并发问题中的可见性问题。

有序性

有序性指的是程序按照代码的先后顺序执行。编译器为了优化性能,有时候会改变程序中语句的先后顺序,但是不会影响最终的执行结果。有序性比较经典的例子就是利用双重检查创建单例对象。

在Java创建对象的时候主要进行以下几个步骤:

(1)分配一块内存区域W;

(2)在内存W上初始化 Singleton 对象;

(3) 然后 W 的地址赋值给 instance 变量。

但是Java在编译的时候会进行编译优化,把方法的执行顺序修改成

(1)分配一块内存区域W;

(2) 然后 W 的地址赋值给 instance 变量。

(3)在内存W上初始化 Singleton 对象;

如果线程A按照优化好的逻辑执行到第(2)步骤给开辟的内存地址分配给instance对象后,线程B的请求也打过来,此时getInstance()方法时 instance 是不等于null的,线程B会认为已经创建好单例对象,直接返回,后面的业务代码在进行对象操作的时候会出现空指针的问题。具体流程如下图所示。
在这里插入图片描述

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-09-11 18:40:35  更:2021-09-11 18:42:27 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 17:14:29-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码