Java 高效并发

Java 高效并发

为了便于移植,Java 多线程内存模型不与硬件关联,不同硬件平台可以使用不同的实现手段

和 CPU 内存与高速缓存做对比 Java 内存模型被分为两大部分:主内存(对应 PC 内存)和工作内存(对应 CPU 高速缓存)

主内存与工作内存之间数据的交互 Java 定义了以下 8 种原子操作(最新的 Java 标准已经采用了新的内存访问协议,但下面 8 中操作也应该了解一下)

  1. lock,标识主内存变量为线程独占

    1. 同一个变量可以被一条线程多次 lock,但也需要同样次数的 unlock 才能解锁
    2. lock 一个变量时会清空工作内存中此变量的值,在使用这个变量前需要执行 load 和 assign 初始化变量
  2. unlock,释放主内存被锁变量
    1. 一个变量实现没有被 lock,那也不允许执行 unlock
    2. 对一个变量执行 unlock 之前,必须先把变量同步到主内存中
  3. read,从主内存中读取变量到工作内存中,以便后续的 load 操作
  4. load,作用于工作内存,把 read 操作从主内存中得到的变量值放入工作内存的变量副本中
  5. use,把工作内存中的变量传递给执行引擎
    1. 只有对一个变量前一个操作是 load 时,当前线程才可以使用 use;只有对一个变量后一个操作是 use 时才可以对变量执行 load 操作
  6. assign,把一个从执行引擎接收到的值赋予工作内存中的变量
    1. 变量在工作内存中改变之后必须把变化同步回主内存
  7. store,把工作内存中的变量传递到主存中
    1. 只有对变量执行 assign 操作后才能执行 store;之后后一个操作是 store 才可以执行 assign 操作
  8. write,作用于主内存,把 store 操作从工资内存中得到的变量值放入主内存中

结合上面 8 中操作如果把一个变量从主内存复制到工作内存,那就要顺序执行 read 和 load 操作,Java 内存模型只要求上述两个操作必须按顺序执行(且不能单独出现 read 或 load,同理 write 和 store),没有要求连续执行,即这两个操作之间可以插入其他指令

volatile

volatile 变量和 C/C++ 中的概念是一致的,有以下两个特点

  1. 对所有线程可见,使用 volatile 变量时所有内存会从主存中刷新这个变量,因为线程对 volatile 变量的使用不是互斥的所以 volatile 变量无法保证线程安全,若要保持 volatile 变量的原子性,需要使用同步手段
  2. 禁止指令重排优化

Java 内存模型要求 lock 等 8 个变量操作都具有原子性,但对 64 位数据类型却定义了比较宽松的规定:允许虚拟机将没有被 volatile 修饰的数据读写操作分为两次 32 位的操作来运行。这就导致了多线程非同步情况下读到半个变量的可能性 ,不过大部分商用虚拟机实现都将 64 位数据的操作也实现为原子操作

Java 与线程

JDK 1.2 前 Java 的线程使用协程实现,之后使用系统原生线程

线程安全与锁优化

互斥是方法,同步是目的。Java 中最基本的互斥同步手段是关键字 synchronized

悲观锁

常见的互斥锁是悲观锁,认为只要使用变量就要上锁,无论变量是否出现了竞争条件。随着 CPU 指令的发展我们可以使用基于冲突检测的并发策略,也就是乐观锁。通俗的讲,先进行操作,如果没有发现竞争就认为操作成功,否则就采取其他补偿措施,比如不断的重试,直到成功为止

自旋锁

线程的挂起与恢复是非常耗时的,如果上锁时间很短,使用自旋锁是非常好的优化手段

自适应自旋锁,自旋的次数按一定的策略动态变化

乐观锁

乐观锁与悲观锁最大的区别是前者会先尝试去修改变量,失败后进行补偿,乐观锁避免了线程的阻塞

CAS

乐观锁示例:CAS(Compare And Swap),下面这段代码是 Java 使用 CAS 实现的变量自增,可以用来说明 CAS 的使用方法

public final int incrementAndGet()
{
    for(;;)
    {
        int current = get();
        int next = current+1;
        if(compareAndSet(current, next)) // 非互斥,设置失败则不断尝试
        {
            return next;
        }
    }
}
CAS 的 ABA 漏洞

两个线程分别使用 CAS 实现变量 val 的修改,假设 val 初始值为 val0,线程 A 将 val 修改为 val1 后又修改为 val0,如果在这个过程中线程 B 先读到的是 val0,在修改时 A 已经完成了 val 从 val1 到 val0 的修改过程,val 的状态其实已经发生了变化,但 B 却没有感知到,这个漏洞被称为 ABA。大部分情况下 ABA 对程序的正常运行没有影响

Java 轻量级锁

Java 中的轻量级锁是和系统提供的锁相对应的,本意是在没有多线程竞争的前提下减少传统重量级锁的使用,以减少互斥带来的损耗

轻量级锁所依据的前提是:对于绝大部分的锁,在整个同步周期内都是不存在竞争的,所以没必要一定要使用重量级锁

轻量级锁会先尝试使用 CAS 给对象打标记,如果成功就不用调用重量级锁并标记对象已被其他线程占用;而其他线程在使用对象时也会确认对象是否已被占用

在存在竞争的情况下,轻量级锁会比重量级锁耗时

原文地址:https://www.cnblogs.com/jiahu-Blog/p/11824847.html

时间: 2024-10-11 02:01:56

Java 高效并发的相关文章

Java高效并发

一段时间没有回顾多线程相关知识了,虽然工作中会用到一些多线程的内容,但都偏向于基础,今天重读多线程相关内容,发现有些东西还是需要注意下.这些一般是面试高频问题奥. 了解并发的内幕是一个高级程序员不可缺少的课程 Java内存模型 注意,Java内存模型(JMM)和JVM运行时数据区不是同一个概念,还有一个概念是Java对象模型下次可以单独拿出来说. JMM都是围绕着原子性,可见性,有序性来讲的 JMM定义了JVM如何与计算机的内存进行交互 线程对变量的所有操作都需要在工作内存中完成,不可直接操作主

Java高并发秒杀API之业务分析与DAO层

课程介绍 高并发和秒杀都是当今的热门词汇,如何使用Java框架实现高并发秒杀API是该系列课程要研究的内容.秒杀系列课程分为四门,本门课程是第一门,主要对秒杀业务进行分析设计,以及DAO层的实现.课程中使用了流行的框架组合SpringMVC+Spring+MyBatis,还等什么,赶快来加入吧! 第1章 课程介绍 本章介绍秒杀系统的技术内容,以及系统演示.并介绍不同程度的学员可以学到什么内容. 第2章 梳理所有技术和搭建工程 本章首先介绍秒杀系统所用框架和技术点,然后介绍如何基于maven搭建项

Java 多线程并发编程面试笔录一览

知识体系图: 1.线程是什么? 线程是进程中独立运行的子任务. 2.创建线程的方式 方式一:将类声明为 Thread 的子类.该子类应重写 Thread 类的 run 方法 方式二:声明实现 Runnable 接口的类.该类然后实现 run 方法 推荐方式二,因为接口方式比继承方式更灵活,也减少程序间的耦合. 3.获取当前线程信息? Thread.currentThread() 4.线程的分类 线程分为守护线程.用户线程.线程初始化默认为用户线程. setDaemon(true) 将该线程标记为

Java高并发程序设计

一.并行世界 摩尔定律的失效,2004年秋季,Intel宣布彻底取消4GHz计划,CPU向多核心的发展,顶级计算机科学家唐纳德丶尔文丶克努斯评价:在我看来,这种现象(并发)或多或少是由于硬件设计者已经无计可施导致的,他们将摩尔定律失效的责任推脱给软件开发者. 1.几个概念 同步和异步: 并发与并行:都表示多个任务同时执行,但并发偏重于多个任务交替执行. 临界区:即共享的数据 阻塞和非阻塞: 死锁.饥饿和活锁:死锁为互不让行,饥饿为一直拿不到资源,活锁为一直撞到 2.并发级别   由于临界区的存在

Java:并发不易,先学会用

我从事Java编程已经11年了,绝对是个老兵:但对于Java并发编程,我只能算是个新兵蛋子.我说这话估计要遭到某些高手的冷嘲热讽,但我并不感到害怕. 因为我知道,每年都会有很多很多的新人要加入Java编程的大军,他们对"并发"编程中遇到的问题也会有感到无助的时候.而我,非常乐意与他们一道,对使用Java线程进行并发程序开发的基础知识进行新一轮的学习. 本篇我们来谈谈 Java 并发编程:并发编程并不容易掌握,但很有必要掌握. 01.我们为什么要学习并发? 我的脑袋没有被如来佛祖开过光,

高效并发(四)

前言 ? ? ? ?上篇已经分析了lock和concurrent提供的集合类包括阻塞队列和容器类.这里我们来介绍 Executor框架 线程池的作用 ? ? ? ?线程池作用就是限制系统中执行线程的数量.根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果:少了浪费了系统资源,多了造成系统拥挤效率不高.用线程池控制线程数量,其他线程 排队等候.一个任务执行完毕,再从队列的中取最前面的任务开始执行.若队列中没有等待进程,线程池的这一资源处于等待.当一个新任务需要运行时,如果线程 池中

2017.4.26 慕课网--Java 高并发秒杀API(一)

Java高并发秒杀API系列(一) -----------------业务分析及Dao层 第一章 课程介绍 1.1 内容介绍及业务分析 (1)课程内容 1 SSM框架的整合使用 2 秒杀类系统需求理解和实现 3 常用技术解决高并发问题 (2)SSM框架 (3)为何选择秒杀系统 1 秒杀系统具有典型的"事务"特性 2 秒杀/红包类需求越来越常见 3 面试常用问题 1.3 项目效果演示 第二章 梳理所有技术和搭建工程 2.1 相关技术介绍 2.2 创建项目和依赖 第三章 秒杀业务分析 3.

Java高效读取大文件

1.概述 本教程将演示如何用Java高效地读取大文件.这篇文章是Baeldung (http://www.baeldung.com/) 上“Java——回归基础”系列教程的一部分. 2.在内存中读取 读取文件行的标准方式是在内存中读取,Guava 和Apache Commons IO都提供了如下所示快速读取文件行的方法: Files.readLines(new File(path), Charsets.UTF_8); FileUtils.readLines(new File(path)); 这种

Java IO: 并发IO

原文链接 作者: Jakob Jenkov 译者: 李璟 有时候你可能需要并发地处理输入和输出.换句话说,你可能有超过一个线程处理输入和产生输出.比如,你有一个程序需要处理磁盘上的大量文件,这个任务可以通过并发操作提高性能.又比如,你有一个web服务器或者聊天服务器,接收许多连接和请求,这些任务都可以通过并发获得性能的提升. 如果你需要并发处理IO,这里有几个问题可能需要注意一下: 在同一时刻不能有多个线程同时从InputStream或者Reader中读取数据,也不能同时往OutputStrea