惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

W
WeLiveSecurity
The Last Watchdog
The Last Watchdog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
G
Google Developers Blog
博客园 - 叶小钗
雷峰网
雷峰网
人人都是产品经理
人人都是产品经理
博客园_首页
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园 - 三生石上(FineUI控件)
Help Net Security
Help Net Security
Cloudbric
Cloudbric
AI
AI
N
News | PayPal Newsroom
博客园 - 聂微东
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 【当耐特】
Forbes - Security
Forbes - Security
美团技术团队
Stack Overflow Blog
Stack Overflow Blog
SecWiki News
SecWiki News
H
Heimdal Security Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
MyScale Blog
MyScale Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
P
Proofpoint News Feed
S
Security @ Cisco Blogs
Google DeepMind News
Google DeepMind News
V
V2EX
大猫的无限游戏
大猫的无限游戏
阮一峰的网络日志
阮一峰的网络日志
S
Security Affairs
L
LangChain Blog
The Hacker News
The Hacker News
F
Full Disclosure
aimingoo的专栏
aimingoo的专栏
Hacker News - Newest:
Hacker News - Newest: "LLM"
腾讯CDC
Webroot Blog
Webroot Blog
A
About on SuperTechFans
H
Hacker News: Front Page
Cyberwarzone
Cyberwarzone
WordPress大学
WordPress大学
L
LINUX DO - 热门话题
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Attack and Defense Labs
Attack and Defense Labs
M
MIT News - Artificial intelligence

青空之蓝

[青空之蓝-2023] - 色彩 | 青空之蓝 [青空之蓝-2022] - 平静 | 青空之蓝 [青空之蓝-2021] - 远望 | 青空之蓝 浅谈垃圾回收 | 青空之蓝 浅谈泛型擦除 | 青空之蓝 浅谈单点登录 | 青空之蓝 使用 Kotlin 编写 Spring 测试 | 青空之蓝 设计模式系列文章 | 青空之蓝 从零实现一个 Java 微框架 - IoC | 青空之蓝 从零实现一个 Java 微框架 - 前言 | 青空之蓝 浅谈 JVM:类加载 | 青空之蓝 浅谈 IO | 青空之蓝 浅谈并发:synchronized & ReentrantLock | 青空之蓝 浅谈并发:CAS & AQS | 青空之蓝 浅谈并发:ThreadLocal | 青空之蓝 浅谈组合注解 & 注解别名 | 青空之蓝 [青空之蓝-2020]-迷茫 | 青空之蓝 Java 系列文章 | 青空之蓝 HTTP 系列文章 | 青空之蓝 浅谈 EatWhatYouKill | 青空之蓝 浅谈可扩展线程池 | 青空之蓝 聊聊写框架 | 青空之蓝 聊聊现状-[2020-09] | 青空之蓝 浅谈并发:锁 | 青空之蓝 浅谈并发:基础 | 青空之蓝 浅谈缓存 | 青空之蓝 无须定义类,Spring 快速注入 Json 参数 | 青空之蓝 浅谈 Proxy 和 Aop | 青空之蓝 从零实现一个 PHP 微框架 - 初始化请求 | 青空之蓝 为 Vue3 添加一个简单的 Store | 青空之蓝 从零实现一个 PHP 微框架 - 服务提供者 | 青空之蓝 WSL2 踩坑记录 | 青空之蓝 浅谈浏览器Event Loop [更新] | 青空之蓝 从零实现一个 PHP 微框架 - Bootstrap 启动加载 | 青空之蓝 从零实现一个 PHP 微框架 - IoC 容器 | 青空之蓝 从零实现一个 PHP 微框架 - PSR & Composer | 青空之蓝 从零实现一个 PHP 微框架 - 前言 | 青空之蓝 MVVM 简单实现 | 青空之蓝 浅谈 DI 和 IoC | 青空之蓝 中间件实现 [PHP] | 青空之蓝 告别 Windows 终端的难看难用,打造好用的 PowerShell | 青空之蓝 VSCode Java输出中文乱码问题解决[更新] | 青空之蓝 浅谈浏览器渲染 | 青空之蓝 Vue-Cli@2 项目迁移日志 | 青空之蓝 Laragon & Scoop 集成踩坑记录 | 青空之蓝 「一行代码」优雅管理 Windows 软件 | 青空之蓝 [青空之蓝-2019]-年度总结 | 青空之蓝 为Vue添加简单的Store | 青空之蓝 为React添加简单的Store | 青空之蓝 为Vuex添加同步Action | 青空之蓝 浅谈B+树 | 青空之蓝 浅谈跳表 | 青空之蓝 浅谈数据库索引 | 青空之蓝 MySQL事务隔离 | 青空之蓝 算法复杂度分析(1) | 青空之蓝 一年来的经验总结 | 青空之蓝 Acrylic - VSCode Extension | 青空之蓝 ace编辑器设置惯性滚动 | 青空之蓝 Java二叉树实现 | 青空之蓝 Java图实现 | 青空之蓝 为apt方式安装的nginx重新编译增加WebDAV | 青空之蓝 XK-Editor - 一个支持富文本和Markdown的编辑器 | 青空之蓝 JS生成列表树 | 青空之蓝 Laravel生成目录树 | 青空之蓝 XK-Note - 集各种神奇功能的云笔记 | 青空之蓝 PHP GD生成验证码 | 青空之蓝 PHP GD图片处理[转换格式-水印-缩略图] | 青空之蓝 Origami - 简洁轻快的WordPress主题 | 青空之蓝 为WordPress启用WorkBox | 青空之蓝 [青空之蓝-2018]-年度总结 | 青空之蓝 VSCode Java手动导入jar和源码包 | 青空之蓝 Windows IP变化自动发送邮件 | 青空之蓝 C链表实现重制版 | 青空之蓝 C 结构体的定义和使用 | 青空之蓝 图的搜索(遍历) - BFS & DFS | 青空之蓝 Java链表实现 | 青空之蓝 C 快速排序 | 青空之蓝 C 插入排序 | 青空之蓝 C 归并排序 | 青空之蓝 C语言链表实现 | 青空之蓝 VSCode配置Java调试环境[Windows] | 青空之蓝 C 选择排序 | 青空之蓝 C 冒泡排序 | 青空之蓝 VSCode配置PHP调试环境[Windows] | 青空之蓝 VSCode配置C/C++ GDB调试环境[Windows] | 青空之蓝 WordPress友情链接模板 | 青空之蓝 Intel Optane 傲腾内存体验 | 青空之蓝 Mysql双机热备实战 | 青空之蓝 博客一年记录 | 青空之蓝 为WordPress启用Service Worker | 青空之蓝 Bing每日一图API | 青空之蓝 iframe延迟加载 | 青空之蓝 写在2018年高考前 | 青空之蓝 The Fox主题汉化分享 | 青空之蓝 [青空之蓝-2017]-崭新 | 青空之蓝 本博客评论规则 | 青空之蓝 世界,您好! | 青空之蓝
浅谈并发:三大特性 | 青空之蓝
Otstar Lin · 2021-02-09 · via 青空之蓝

浅谈并发:三大特性

前言

好久没写并发相关的文章了,之所以一直没写是因为我也是半桶水水平,一直写不出来,最近使用了 Obsidian 来记笔记(之前一直没笔记习惯,学了就忘了,所以打算用一款好的软件尝试下),积累了一些笔记,便打算通过写文章加深下,顺便看看这种 卡片式笔记法 对整理思路的是否有帮助。

可见性

可见性 指的是一个主内存的线程如果进行了修改,可以及时被其他线程观察到。

可见性问题产生的原因

缓存导致了可见性问题,在 Java 中所有变量都储存在主内存中,- 每个线程都有自己独立的工作内存(缓存、寄存器等),里面保存该线程使用到的变量的副本(主内存中该变量的一份拷贝),线程对共享变量所有的操作都必须在自己的工作内存中进行,不能直接从主内存中读写,不同线程之间也无法直接访问其他线程的工作内存中的变量,线程间变量值的传递需要通过主内存来进行。

样例

private boolean stop = false;

public void stop() {
	stop = true;
}

public void loop() {
	while (!stop) {
		// do something
	}
}
private boolean stop = false;

public void stop() {
	stop = true;
}

public void loop() {
	while (!stop) {
		// do something
	}
}

问题产生

以上代码在运行前,各线程会先将 stop 值复制到自己的工作内存中,当线程 1 进入 loop 的循环的时候该线程会一直从自己的工作内存读取值,并不会再从主内存读取值。这样就造成了一个问题,如果 stop 方法不是线程 1 调用的,那么线程 1 读取到的值则会一直为 false 造成死循环的发生。

解决方案

  • volatile
  • synchronized

对于可见性问题,我们可以为 stop 变量加上volatile关键字,这样线程 1 每次都会到主内存读取 stop 值,其他线程更新时也会立即更新到主内存,这样就可以及时停止死循环。

volatile 通过加入内存屏障和禁止重排序优化来实现。对 volatile 变量写操作时,会在写操作后加入一条 store 屏障指令,将本地内存中的共享变量值刷新到主内存。对 volatile 变量读操作时,会在读操作前加入一条 load 屏障指令,从主内存中读取共享变量。

private volatile boolean stop = false;

public void stop() {
	stop = true;
}

public void loop() {
	while (!stop) {
		// do something
	}
}
private volatile boolean stop = false;

public void stop() {
	stop = true;
}

public void loop() {
	while (!stop) {
		// do something
	}
}

除了 volatile 关键字,我们还可以使用 synchronized 加锁。这是因为线程解锁前,必须把共享变量的最新值刷新到主内存。线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存重新读取最新的值。

完整样例:可见性 & 原子性问题及解决

有序性

有序性指的是程序按照代码的先后顺序执行。

有序性问题产生的原因

指令重排导致了有序性问题,Java 内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。

样例

public class Singleton {

  static Singleton instance;

  static Singleton getInstance() {
    if (instance == null) {
      synchronized (Singleton.class) {
        if (instance == null) {
          instance = new Singleton();
        }
      }
    }
    return instance;
  }
}
public class Singleton {

  static Singleton instance;

  static Singleton getInstance() {
    if (instance == null) {
      synchronized (Singleton.class) {
        if (instance == null) {
          instance = new Singleton();
        }
      }
    }
    return instance;
  }
}

问题产生

未进行指令重排的情况下,instance = new Singleton() 的执行顺序如下:

  1. 分配一块内存 M
  2. 在内存 M 上初始化 Singleton 对象
  3. 然后 M 的地址赋值给 instance 变量

然而编译器有可能进行以下的指令重排,重排后的顺序如下:

  1. 分配一块内存 M
  2. 将 M 的地址赋值给 instance 变量
  3. 然后在内存 M 上初始化 Singleton 对象

当指令重排后,线程 1 instance = new Singleton() 执行完 2 步骤后,此时进行线程切换,线程 2 getInstance() 当走到 if (instance == null) 的时候,由于 instance 变量已经被赋值了,那么就获取成功,返回,但是此时 Singleton 对象并未初始化,如果这时候使用该对象,那么就会导致抛出 NPE 异常。

解决方案

  • volatile
  • synchronized

instance 变量加上 volatile 关键字禁用指令重排即可。

public class Singleton {

  static volatile Singleton instance;

  static Singleton getInstance() {
    if (instance == null) {
      synchronized (Singleton.class) {
        if (instance == null) {
          instance = new Singleton();
        }
      }
    }
    return instance;
  }
}
public class Singleton {

  static volatile Singleton instance;

  static Singleton getInstance() {
    if (instance == null) {
      synchronized (Singleton.class) {
        if (instance == null) {
          instance = new Singleton();
        }
      }
    }
    return instance;
  }
}

原子性

原子性指提供互斥访问,同一时刻只能有一个线程对数据进行操作。简单来说就是序列化的执行。

问题原因

线程切换导致原子性问题,Java 并发程序都是基于多线程的,操作系统为了充分利用 CPU 的资源,将 CPU 分成若干个时间片,在多线程环境下,线程会被操作系统调度进行任务切换。

样例

private int count = 0; //1

public void add() {
    count++; // 2
}

public int get() {
    return count; // 3
}
private int count = 0; //1

public void add() {
    count++; // 2
}

public int get() {
    return count; // 3
}

问题产生

以上的代码除了 1 是原子操作,其他均不是原子操作。

其实语句 2 在执行的时候,包含三个指令操作:

  1. count 的值加载到 CPU 的寄存器
  2. 进行 +1 操作
  3. 将计算结果写入到 count 内存中

对于上面的三条指令来说,如果线程 1 在 1 执行完后做线程切换,线程 1 和线程 2 按照下图的序列执行,那么我们会发现两个线程都执行了 count+=1 的操作,但是得到的结果不是我们期望的 2,而是 1。

解决方案

  • Atomic
  • CAS
  • synchronized
  • Lock

对原子性不安全的地方进行加锁操作使用原子操作类CAS 等方法即可解决原子性的问题。如样例的代码经过如下改动即可实现线程安全:

private int count = 0; //1

public synchronized void add() {
    count++; // 2
}

public synchronized int get() {
    return count; // 3
}
private int count = 0; //1

public synchronized void add() {
    count++; // 2
}

public synchronized int get() {
    return count; // 3
}

当然对于这种累加的操作我们最好还是使用对应的原子类,如 int 对应 AtomicInteger

完整样例:可见性 & 原子性问题及解决

结语

到此这篇文章就结束了,虽说我们了解了这些问题产生的原因和解决方案,但是在实际的编码过程中,并发问题往往都很隐蔽,不容易发现和修正。除了隐蔽外,并发编程也存在着心智负担,我们通常很难决定是要使用那种同步方式来防止线程不安全的情况发生,比如我在写框架的时候就经常遇到是要暴力加锁还是用 CAS 或者并发容器,是要弱一致性还是要强一致性。

如果您发现了文章的错误欢迎留言纠正。

浅谈并发:三大特性

https://blog.ixk.me/post/talking-about-concurrency-three-characteristics
  • 许可协议

    BY-NC-SA

  • 本文作者

    Otstar Lin

  • 发布于

    2021/02/09

转载或引用本文时请遵守许可协议,注明出处、不得用于商业用途!

浅谈并发:ThreadLocal浅谈组合注解 & 注解别名