带你读懂《Java并发编程》:第3章 助于线程安全的三剑客:final&valitale&线程封闭...

C++多线程并发总结 文章目录线程创建与管理并发与并行多线程并发与多进程并发C++线程创建线程创建的简单示例线程同步之互斥锁lock与unlock保护共享资源lock_guard与unique_lock保护共享资源timed_mutex与recursive_mutex提供更强大的锁线程同步之条件变量异步编程使用promise与future传递结果使用packaged_task与future传递结果 线程创建与管理 并发... 阅读详情

点击上方蓝字关注我们

我们简要的回顾前文:

《第1章 多线程安全性与风险》介绍了并发编程,在维护难度、性能以及活跃性三个方面,所带来的风险与优势;

《第2章 影响线程安全性的原子性和加锁机制》介绍了并发编程核心概念 -- 线程安全性,介绍了用同步的手段,来避免多个线程在同一时刻访问相同的数据;

今天分享的是《Java并发编程实战》第3章 -- “对象的共享”:介绍用安全共享和发布对象的手段,来让多个线程能够安全的同事访问同一数据。

开始前,我们先回顾下几个重要的基础知识:缓存一致性 和 Java内存模型。

现实计算机系统是存在缓存一致性问题的,见下图:

  • 现实因素:由于计算机存储设备与处理器的运算速度有个数量级的差距(即便存储设备使用SSD,还是比处理器慢几个数量级),所以计算机系统不得不增加一层读写速度尽可能接近处理器速度的高速缓存Cache来作为内存和处理器之间的缓冲。

  • 缓存一致性(Cache Coherence):基于高速缓存的存储交互解决了处理器和内存的速度矛盾,但是引入了缓存一致性(在多处理器系统中,每个处理器都有自己的高速缓存,而它们又共享同一个主内存(Main Memory),当多个处理器的运算任务都涉及同一块主内存区域时,将可能导致各自的缓存数据不一致)。

  • Java内存模型(Java Memory Model,JMM):基于现实因素制约造成的内存并发不一致,JVM 定义了一套 JMM 来屏蔽掉各种硬件和操作系统的内存访问差异,以达到统一共和,哦不,是让 Java 程序一处编译到处运行,实现在各种平台下都能达到一致的内存访问效果。

    1)JMM:规定所有变量都存在主内存(Main Memory)了,每条线程还有自己的工作内存(Working Memory),工作内存保存了变量的主内存拷贝。

2)JMM 内存模型里,从Java线程 -> 工作内存 -> 主内存之间的1次读写分为8个基本操作:(篇幅所限不展开,后续文章会跟进讲解)

操作作用
lock (锁定)作用于主内存的变量,把一个变量标识为线程独占状态
unlock (解锁)

作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定

read (读取)作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用
load (载入)

作用于工作内存的变量,它把read操作从主存中变量放入工作内存中

use (使用)作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令
assign (赋值)作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中
store (存储)作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用
write (写入)作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中

3)JMM的内存间交互操作时序见下图:

对象的可见性

我们通过下面的例子来认识下潜在的变量可见性问题。

例子1:变量的可见性不暴露于多线程

/**
* <p>可见性测试</p>
 */
public class NoVisibility {
//非线程安全的状态变量
private static boolean ready;
private static int number;


private static class ReaderThread extends Thread{
@Override
public void run() {
while (!ready)
//挂起,让出CPU占用权
        Thread.yield();
      System.out.println("ready = " + ready + " , and number = " + number);
    }
  }


public static void main(String[] args) throws InterruptedException {
new ReaderThread().start();
    number=43;
    ready=true;
  }
}


例子1 输出结果:大部分情况下,我们运行程序可以得到下面的输出:

ready = true , and number = 43

例子1 代码解析:

主线程A开启了一个异步线程B(B负责循环校验 ready变量,如果为false,继续让出CPU所有权空轮转,如果为true,打印日志)。这个程序的大部分情况运行结果都正确,但它并非线程安全。

  • 上面的 NoVisibility 类可能会无限循环下去,因为 ready 值可能对Reader线程来说一直是不可见的。

  • 在更为特殊的情况下,NoVisibility甚至可能会打印出0,因为可能会在写入number之前写入ready值,这是由于著名的“指令重排序”(reordering)现象。

Q1:指令重排序是什么?

指令是指示计算机执行某种操作的命令,如:数据传送指令、算术运算指令、位运算指令、程序流程控制指令、串操作指令、处理器控制指令。

Java语言规范JVM线程内部维持顺序化语义:

只要程序的最终结果与它顺序化情况的结果相等,那么指令的执行顺序可以与代码逻辑顺序不一致,这个过程就叫做指令的重排序。    

指令重排序的意义:使指令更加符合CPU的执行特性,最大限度的发挥机器的性能,提高程序的执行效率。

代码优化:只要数据在多个线程之间共享,就使用正确的同步。

维护可见性的两种手段

维护可见性也有两种手段:一是同步锁,二是使用volatile关键字。

维护可见性之一:同步锁使用。

例子1 的NoVisiability 展示了缺乏同步的程序可能产生的错误情况:失效数据。失效数据可能不会同时出现,一个线程可能获取新值,而另一个获取旧值。我们看下例子2和例子3:

反例2:线程不安全的例子

@NotThreadSafe
public class MutablInteger {
private int value;


public int getValue() {
return this.value;
  }


public void setValue(final int value) {
this.value = value;
  }
}

正例3:使用同步锁优化后的例子

@ThreadSafe
public class SynchronizedInteger {
private int value;


public synchronized int getValue() {
return this.value;
  }


public synchronized void setValue(final int value) {
this.value = value;
  }
}

例子2、3的代码解析:

  • 我们通过对getter/setter方法同时加锁,确保所有线程都在拥有同步的情况下访问value。

加锁的含义不仅仅局限于互斥行为,还包括内存可见性。为了确保所有线程都能看到共享变量的最新值,所有执行读操作或者写操作的线程都必须在同一个锁上同步。

维护可见性之二:volatile 变量。

例子2和例子3,其实在没有同步的情况下,可能会得到一个失效值,但至少是由之前的线程设置的,不是随机值,此称为“最低安全性”(out-of-thin-airsafety)。可是,最低安全性适用于绝大多数变量,但不适用于非volatile类型的64位数值变量。因此我们需要使用 volatile 来声明它们或者用锁保护起来。

JMM 要求,变量的读取操作和写入操作都必须是原子操作,但对于非volatile类型的long和double变量,JVM允许将64位的读操作或写操作分解为两个32位的操作。 

当读取一个非volatile类型的long变量时,如果对该变量的读操作和写操作在不同的线程中执行,那么很可能会读取到某个值的高32位和另一个值的低32位。

Volatile变量

Java语言提供了一种稍弱的同步机制,即volatile变量,用来确保变量的更新操作通知到其他线程。

  • 禁止重排序:当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。

  • 读取写入作用于主内存:volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。

例子4:volatile 的使用

public class CountSheeps {
  static volatile boolean asleep;
  public static void main(String[] args) throws InterruptedException {
    new CountSheepsThread().start();
    Thread.sleep(10000);
    asleep = true;
  }
  private static class CountSheepsThread extends Thread{
    @Override
    public void run() {
      while (!asleep)
        Thread.yield();
      countSheep();
    }
    private void countSheep() {
      System.out.println("zzz...");
    }
  }
}

例子4分析:

在例子4中,主线程A在休眠10s后,将 asleep 变量的值修改为true,在A休眠的10s里,线程B一直处于轮训中的可执行状态,直到asleep的值被修改。换句话说,asleep 是线程A和B的共享变量,利用volatile修饰之后,A线程的改动会及时通知B线程,进而B线程执行后续业务。

给volatile做个总结:

加锁机制既可以确保可见性又可以确保原子性,而volatile变量只能确保可见性。

《Java并发编程》建议当且仅当满足以下所有条件时(相当苛刻的条件),才应该使用volatile变量:

  • 变量的写入操作不依赖变量的当前值,或者你能确保只有单个线程更新变量的值。

  • 该变量不会与其他状态变量一起纳入不变性条件中。 

  • 在访问变量时不需要加锁。

对象的发布/逸出

对象的“发布(Publish)”是指:一个对象能够在当前作用域之外的代码中使用。对象的“逸出(Escape)”是指:当某个不应该发布的对象被发布时,这种情况就被称为。常见的发布对象的手段有两种:

  • 第一种发布对象方式是:将对象那个的引用保存到一个公有的静态变量中,以便任何类和线程都能看见该对象。

例子5:不安全的对象发布

@NotThreadSafe
public class PublishObject2 {
  private String[] states = new String[] {"AK","AL"};
  public String[] getStates() { return states; }
}

例子5 解析:

由于states逸出了它所在的作用域,因为这个本应是私有的变量已经被发布了,导致任何调用者都可以修改这个数组的内容。
 

  • 第一种发布对象方式是:发布一个内部的类实例。

例子6:也是不安全的对象发布。

/**
 * <p>内部类,this在构造函数中逸出</p>
 */
@NotThreadSafe
public class ThisEscape {
  public static void main(String[] args) {
    EventSource eventSource = new EventSource();
    new ThisEscape(eventSource);
  }
  // ThisEscape 对象的构造器
  public ThisEscape(EventSource source) {
    source.registerListener(
        new EventListener() {
          //发布匿名对象(也发布了ThisEscape实例)
          public void onEvent(Event e) {
            doSomething(e);
          }
        },this);
  }
  private void doSomething(Event e) {
  }
}
public class EventSource {
  public void registerListener(EventListener eventListener, ThisEscape name) {
    System.out.println("EventSource知道了这个类,也会将它告诉 EventListener");
    eventListener.esclape(name);
  }
}
public class EventListener {
  public void onEvent(Event e) {
  }
  public void esclape(ThisEscape name) {
    System.out.println("这是不安全的对象发布,因为EventSource的告知, EventListener 也知道 ThisEscape 被构建了:" + name.getClass());
  }
}

例子6 输出结果:

EventSource知道了这个类,也会将它告诉 EventListener
这是不安全的对象发布(可以在一个对象的构造器里做点别的事情,该对象未完全构造完成,就可以被别的地方调用,因此属于逸出现象),
因为EventSource的告知, EventListener 也知道 ThisEscape 被构建了:class shizhanthread.chapter3.ThisEscape

例子6 解析:

由于在构造器中进行内部类的创建,导致该构造器的对象同样被“无意中发布了”。

隐式或者显示的使用this引用,会导致构造器拥有者逸出,因此,不要在构造过程中使this引用。

线程封闭

上面提到的对象发布和逸出,是为了合理处理对象作用域的范围,说到底是为了不让它被其它线程随意修改数据和状态。那有没有断绝其他线程干扰的方法呢?

有的,避免使用同步的方式就是不共享数据,即:线程封闭(Thread Confinement)。

线程封闭(Thread Confinement)技术:只在单线程内访问数据,不需要同步,它是实现线程安全性的最简单方式之一。

线程封闭的实现手段有三种:Ad-hoc线程封闭、栈封闭 和 ThreadLocal 类。

Ad-hoc 线程封闭

Ad-hoc线程封闭,用一句大白话说:维护线程封闭性的职责完全由程序实现来承担(你的线程封闭业务逻辑,如果你用Java来写,就用Java的语法来维护,并且逻辑和真实运行结果都要由你自个保证)。

也就是说,这是完全靠实现者控制的线程封闭,因此,也导致Ad-hoc线程封闭非常脆弱,没有任何一种语言特性能将对象封闭到目标线程上。

维基百科上对于Ad hoc这个词有专门的解释“Ad hoc是拉丁文常用短语中的一个短语。这个短语的意思是'特设的、特定目的的、即席的、临时的、将就的、专案的'。

这个短语通常用来形容一些特殊的、不能用于其它方面的的,为一个特定的问题、任务而专门设定的解决方案。这个词汇须与a priori区分。

栈封闭

栈封闭,用一句大白话说,就是局部变量。

多个线程访问一个方法,此方法中的局部变量都会被拷贝一分儿到线程栈中。所以局部变量是不被多个线程所共享的,也就不会出现并发问题(有疑惑的同学,可以回顾开篇提及的 Java内存模型)。所以能用局部变量就别用全局的变量,全局变量容易引起并发问题。

ThreadLocal类

ThreadLocal,用一句大白话说,它是实现线程封闭的最好方法。ThreadLocal内部维护了一个Map,Map的key是每个线程的名称,而Map的值就是我们要封闭的对象。每个线程中的对象都对应着Map中一个值,也就是ThreadLocal利用Map实现了对象的线程封闭。(篇幅所限,后续源码分析再做展开)

ThreadLocal 类提供了get与set等访问接口或方法,这些方法为每个使用该变量的线程都存有一份独立的副本,因此get总是返回由当前执行线程在调用set时设置的最新值。

对象不变性和Final域

对象的不可变性(Immutable Object)是通过 Final域声明,来实现不可变性对象的构造。而被 Final 关键字修饰类型的域是不能修改的,Final 实际上可以用于修饰类/方法/数据域:

  • 类:Final 类不可被继承,无子类,保证用户调用时动作的一致性,可以防止子类覆盖情况的发生。

  • 方法:方法不可被覆盖,private 修饰的方法默认带上了final。

  • 数据域:变量的值为常量,常量的地址不可改变,但在地址指向的内存空间保存的值(即对象的属性)是可以改变的。【本章讲的主要是数据域这个情况】

例子7:在不可变对象中可以使用可变对象管理他们的状态

@Immutable
public final class ThreeStooges {
  //不可变对象的引用,但是Set的数据可以被更新
  private final Set<String> stooges = new HashSet<String>();
  public ThreeStooges() {
    stooges.add("Moe");
    stooges.add("Larry");
    stooges.add("Curly");
  }
  //这是读操作,允许并发读(除非还有并发写的操作,否则就是不安全的)
  public boolean isStooge(String name) {
    return stooges.contains(name);
  }
}

例子7:代码分析

尽管stooges 是保管姓名的Set对象,它是可变的。但从设计来看,在Set对象构造完成后,没有对外暴露任何修改的API,所有的对象状态都通过一个final 域来访问,因此实现了对象不变性。

不可变对象一定是线程安全的。

总结

我们在实战领域编写并发程序,在使用或者共享对象时,《Java并发实战》给我们总结了4条规则:

  • 线程封闭

  • 只读共享。共享的只读对象可以由多个线程并发访问,但任何线程都不能修改它。共享的只读对象包括不可变对象和事实不可变对象

  • 线程安全共享。线程安全地对象在器内部实现同步。

  • 保护对象。被保护的对象只能通过持有特定的锁来方访问。

推荐阅读

《源码系列》

JDK之Object 类

JDK之BigDecimal 类

JDK之String 类

《经典书籍》

Java并发编程实战:第1章 多线程安全性与风险

Java并发编程实战》:第2章 影响线程安全性的原子性和加锁机制

Effective Java 第53条:接口优先于反射机制

Effective Java 第52条:通过接口引用对象

Effective Java 第54条:谨慎的使用本地方法

《服务端技术栈》

Docker 核心设计理念

Kafka史上最强原理总结

HTTP的前世今生

《算法系列》

读懂排序算法(二):希尔排序算法

读懂排序算法(三):堆排序算法

读懂排序算法(四):归并算法

读懂排序算法(五):快速排序算法

扫描二维码

获取技术干货

后台技术汇

点个“在看”表示朕

已阅

多线程并发处理的简单实现 使用队列做缓冲池,通过工作者线程来处理并任务,实现线程空闲时阻塞,有任务令牌时回复服务。 立即下载

相关推荐

YALMIP介绍及怎么在Matlab中加YALMIP、SDPT3

目录YALMIP简介怎么在Matlab中加YALMIP?1. 创建决策变量:2. 添加约束:3. 参数配置4. 求解怎么在MatlabR2019a中加YALMIP?1.下载工具包2.放置工具包3.添加路径4.测试安装结果怎么在MatlabR2019a中加SDPT3? YALMIP简介 怎么在Matlab中加YALMIP? yalmip是一位“集大成者”,它不仅自己包含基本的线性规划求解算法,比如linprog(线性规划)、bintprog(二值线性规划)、bnb(分支界定算法)等,他还提供了对cpl

m0_51431440的博客 2万+

java 关键字 volatile_关于 Java 关键字 volatile 的总结

1 什么是 volatilevolatile 是 Java 的一个关键字,它提供了一种轻量级的同步机制。相比于重量级锁 synchronized,volatile 更为轻量级,因为它不会引起线程上下文的切换和调度。2 volatile 的两个作用可以禁止指令的重排序优化提供多线程访问共享变量的内存可见性3 禁止指令重排3.1 什么是指令重排指令重排序是 JVM 为了优化指令,提高程序运行效率,在不...

weixin_30863333的博客 303

Gamma变换算法之Matlab&FPGA实现

Gamma映射原理 什么是Gamma曲线 人眼的感光与光强并不是呈线性关系的,而是呈非线性关系(指数型关系,如下图左-曲线。在低照度下,人眼更容易分辨出亮度的变化,随着照度的增加,人眼不易分辨出亮度的变化。而图像传感器与输入光强呈线性关系(如下图左-直线)。从图中可见,当传感器感光在20%左右时,人眼感光响应达到了50%,人眼对低照度的变化更为敏感。从下图右侧可见,Reference Tone为人眼感光50%的亮度,而传感器在感光50%时则相对暗的多(见Select,近似中性灰)。这种将原始图像通过映射操

CrazyBingo的博客 7405

Java Volatile 详解

Java Volatile 详解 Volatile:是java虚拟机提供的轻量级的同步机制。保证可见性、禁止指令重排序。不保证原子性!!! 学习Volatile之前必须了解JAVA内存模型。 Java内存模型(Java Memory Model,JMM)本身是一种抽象的概念并不真实存在,它描述的是一组规则或规范,通过这组规范定义了程序中各个变量(包括实例字段,表态字段和构成数组对象的元素)的...

Lailai Monkey的博客 4093

KUKA .RSI 软件包

KUKA RSI软件包通过XML文件使得机器人与外部设备(传感器通讯)通讯成为了可能,但仍存在诸多限制,该模块最多允许9台设备进行连接,有限的IO口,匮乏的功能,并且需要投入大量精力琢磨相关说明文档。为此开发人员针对KUKA实时通讯,做了一些开源的接口使得上位机与机械臂的通讯更为简单。

huhu7777的博客 3290

Clarity 3D Layout

Clarity 3D Layout Clarity 3D layout是3D 全波电磁场模型提取工具,它可以导入已经设计好的PCB文件也可以直接在Clarity 3D layout创建版图进行3D 全波电磁场模型提取。

m0_53280471的博客 5479

Java J.U.C并发包(6) —— 第三章:对象的共享

这里会记录 《Java Concurrency in Practice》(java并发编程实战)的所有知识点哟~ 本章主要内容: 1. 可见性 2. 发布与逸出 3. 线程封闭 4. 不变性 5. 安全发布 第2章介绍的是如何通过同步来避免多线程在同一时刻访问相同的数据,接下来的这一章将介绍如何共享和发布对象,从而使他们能够安全地由多个线程同时访问。这...

想当厨子的程序媛 306

如何用ggplot2绘制专业级双侧柱状图展示GO富集结果(附完整代码)

本文详细介绍了如何使用R语言的ggplot2包绘制专业级双侧柱状图展示GO富集结果,包括数据预处理、基础图形构建、高级样式定制和出版级细节调整。通过完整的代码示例和实用技巧,帮助生物信息学研究人员高效可视化高通量数据,提升学术图表的质量和可读性。

weixin_30342209的博客 75

volatile原理

你真的了解volatile关键字吗? 2017/04/01 | 分类: 基础技术 | 0 条评论 | 标签: volatile 分享到:0 原文出处: Ruheng 一、Java内存模型 想要理解volatile为什么能确保可见性,就要先理解Java中的内存模型是什么样的。 Java内存模型规定了所有的变量都存储在主内存中。每条线程中还有自己的工作内存,线程的工

vip_hitwu的专栏 1988

从立创商城到Cadence:手把手教你实现封装格式的精准迁移

本文详细介绍了如何从立创商城获取元器件封装并精准迁移至Cadence Allegro的全流程。通过实战案例MAX31865ATP+T芯片,逐步解析Altium Designer格式转换、Cadence环境配置、封装导入验证等关键步骤,提供常见问题解决方案和效率提升技巧,帮助硬件工程师实现封装格式的高效迁移。

weixin_42519985的博客 215

Simulink仿真模块 -Scope

如果您将 Scope 模块放在具有锁定链接的库或者锁定的库中,当尝试打开示波器窗口时,Simulink 会显示错误。要显示某个库模块中的内部数据,请为库模块添加一个输出端口,然后将此端口连接到您模型中的 Scope 模块。当您在示波器内可视化多个基于帧的信号时,部分帧大小为 1 的信号采样可能不会显示。当在示波器内可视化多个基于帧的信号时,部分帧大小为 1 的信号采样可能不会显示。如果在仿真开始时波形视图关闭,则当在仿真后打开波形视图时,波形视图将显示附加的输入信号的仿真结果。

jk_101的博客 3789

3_线程安全

Java中的锁

Zelda_xz的博客 357

Python爬虫博客:爬取碳排放交易数据——使用最新技术的完整实现

通过上述方法,我们可以构建一个高效的爬虫,爬取碳排放交易市场的数据并进行分析。这个项目不仅展示了如何处理静态和动态网页,还提供了如何存储数据、分析数据和可视化结果的技巧。随着爬虫技术的不断进步,我们可以进一步提升爬取效率和数据分析能力。如果你希望更深入了解如何进行数据挖掘和机器学习分析,可以尝试将碳排放数据与其他经济数据结合,利用机器学习模型进行趋势预测或决策支持。

2201_76125261的博客 351

c++ 多线程 类成员函数_C++11多线程并发基础入门教程

最后一次更新日期:2020.10.11因为想要将文章按照学习路径和知识的难易程度来排版,所以再在这篇文章中添加大量内容就违背了这个初衷了,因此新开了《C++11多线程并发基础入门教程-进阶版》,这篇文章只会做修订,以及在微调已有的内容,新的内容会跟新到另一篇文章。zizbee:C++11多线程并发基础入门教程-进阶版​zhuanlan.zhihu.com最近在看《C++ Concurrency ...

weixin_39879674的博客 978

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

C++多线程并发(一)---线程管理

一、何为并发 刚开始接触计...

hezhanran的博客 2358

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

线程、进程、多线程、多进程 和 多任务 它们有啥关系

关注+星标公众号,不错过精彩内容作者:TechArtisan6编排:strongerHuang可能学习嵌入式开发的读者都听说过这些专业名词,但又多少人理解了?首先,从定义开始,先看一下教...

strongerHuang 1425

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

__sync_fetch_and_add

最近在公司离职的前辈写的代码哪里看到了__sync_fetch_and_add这个东东.比较好奇.找些资料学习学习 http://www.lxway.com/4091061956.htm http://www.cnblogs.com/FrankTan/archive/2010/12/11/1903377.html 可使用的环境: gcc.version > 4.1.2 作用:提供多...

weixin_30379531的博客 501

java valitale_简单了解java volatile

内存模型基本概念计算机在执行程序时,每条指令都是在CPU中执行的,而执行指令过程中,势必涉及到数据的读取和写入。由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,这时就存在一个问题,由于CPU执行速度很快,而从内存读取数据和向内存写入数据的过程跟CPU执行指令的速度比起来要慢的多,因此如果任何时候对数据的操作都要通过和内存的交互来进行,会大大降低指令执行的速度。因此在CPU里面就有了高速...

weixin_31205797的博客 689

c多线程并发处理方式_Java并发基础,不怕你看不懂

一、前言当我们使用计算机时,可以同时做许多事情,例如一边打游戏一边听音乐。这是因为操作系统支持并发任务,从而使得这些工作得以同时进行。那么提出一个问题:如果我们要实现一个程序能一边听音乐一边玩游戏怎么实现呢?public class Tester { public static void main(String[] args) { System.out.println("开始...."); pla...

weixin_39558804的博客 557

c语言多线程并发编程指南,1.3 C++中的并发和多线程

1.3 C++中的并发和多线程通过多线程为C++并发提供标准化支持是件新鲜事。只有在C++11标准下,才能编写不依赖平台扩展的多线程代码。了解C++线程库中的众多规则前,先来了解一下其发展的历史。1.3.1 C++多线程历史C++98(1998)标准不承认线程的存在,并且各种语言要素的操作效果都以顺序抽象机的形式编写。不仅如此,内存模型也没有正式定义,所以在C++98标准下,没办法在缺少编译器相关...

weixin_39622710的博客 810

Java 多线程 并发编程

一、多线程 1、操作系统有两个容易混淆的概念,进程和线程。 进程:一个计算机程序的运行实例,包含了需要执行的指令;有自己的独立地址空间,包含程序内容和数据;不同进程的地址空间是互相隔离的;进程拥有各种资源和状态信息,包括打开的文件、子进程和信号处理。 线程:表示程序的执行流程,是CPU调度执行的基本单位;线程有自己的程序计数器、寄存器、堆栈和帧。同一进程中的线程共用相同的地址空间,同时共享进

escaflone的专栏 17万+

C语言的多线程编程与并发处理

多线程编程与并发处理在现代软件开发中发挥着关键作用。通过利用C语言中的POSIX线程库以及高级并发工具如CountDownLatch和CyclicBarrier,程序员可以实现高效的并发操作和线程管理。掌握这些技术,可以充分利用多核处理器的性能,提高程序的响应速度和执行效率。从线程的创建、同步与锁机制到高级并发工具的应用,每一步都为实现健壮的并发编程提供了坚实的基础。希望通过本篇文章,读者能够深入理解和掌握C语言的多线程编程和并发处理技术,从而在实践中编写出高性能的并发程序。

weidl001的博客 3007

c多线程并发处理方式_当我们在谈论Go的并发时,实际上在谈论什么?

一、并发(Concurrency)大家都知道,通常情况下一个简单的串行执行的程序在计算机内部被执行的时候是按照指令的顺序一步一步执行,这种情况下CPU在每一条指令必须执行结束才会有下一条指令被执行,整个过程是严格有序的。那么如果遇到某些场景:a. 比如CPU指令执行速度远远大于结果返回的速度时(比如IO操作)时,我们如果还是顺序执行,那么大把的时间就会被浪费,这样在IO操作过程中CPU实际上是处于...

weixin_39951181的博客 351

java volatile_Java中volatile关键字解析

一、内存模型的相关概念大家都知道,计算机在执行程序时,每条指令都是在CPU中执行的,而执行指令过程中,势必涉及到数据的读取和写入。由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,这时就存在一个问题,由于CPU执行速度很快,而从内存读取数据和向内存写入数据的过程跟CPU执行指令的速度比起来要慢的多,因此如果任何时候对数据的操作都要通过和内存的交互来进行,会大大降低指令执行的速度。因此在C...

weixin_39692254的博客 355

并发与多线程

c++11并发与多线程

心愿许得无限大 1986

Spring AI + GPT-5.5 + Ollama 配置教程:Base URL、超时与流式调用

由于不同 Spring AI、Spring Boot 小版本之间仍可能存在差异,创建项目时应检查 Spring Initializr、Spring AI BOM 和官方兼容矩阵,不要直接复制旧教程中的 Starter 名称和版本号。多人项目最好隔离不同客户端,并在脱敏前提下记录最终 URL、状态码、请求耗时、请求 ID 和模型 ID。Spring AI 接入模型 API,至少要同时确认 Starter、Base URL、请求路径、API Key、模型 ID、连接超时、读取超时和流式客户端这 8 项。

2601_95893075的博客 225
上一篇: 一文读懂《Effective Java》第4条:通过私有构造器来强化工具类不可实例化的能力...
下一篇: 带你读懂《Java并发编程》:第3章 助于线程安全的三剑客:final & volatile & 线程封闭...
后台技术汇
博客等级 码龄11年 670粉丝 531原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

后台技术汇

对你的帮助,是对我的最好鼓励。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值