读书笔记-Java并发编程的艺术-第1章 并发编程的挑战

在这里插入图片描述

文章目录

  • 1.1 上下文切换
    • 1.1.1 多线程一定快吗
    • 1.1.2 如何减少上下文切换
  • 1.2 死锁
  • 1.3 资源限制的挑战

1.1 上下文切换

即时是单核处理器也支持多线程执行代码,CPU通过给每个线程分配CPU时间片来实现这个机制。时间片是CPU分配给多个线程的时间,因为时间片非常短,所以CPU通过不停地切换线程执行让我们感觉多个线程是同时执行的,时间片一般是几十毫秒。

CPU通过时间片分配算法来循环执行任务,当前任务执行一个时间片后切换到下一个任务。但是,在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再加载这个任务的状态。所以任务从保存到再加载的过程就是一次上下文切换。

1.1.1 多线程一定快吗

下面的代码演示串行和并发执行并累加操作的时间,并发执行一定比串行执行快吗?

public class ConcurrencyTest {private static final int count = 200000000;public static void main(String[] args) throws InterruptedException {concurrency(count);serial(count);}private static void concurrency(int count) throws InterruptedException {long start = System.currentTimeMillis();Thread thread = new Thread(new Runnable() {@Overridepublic void run() {int a = 0;for (int i = 0; i < count; i++) {a +=5;}}});thread.start();int b = 0;for (int i = 0; i < count; i++) {b--;}thread.join();long time = System.currentTimeMillis() - start;System.out.println("count:" + formatToTenThousand(count) +", concurrency: " + time + "ms,b=" + b);}private static void serial(int count) {long start = System.currentTimeMillis();int a = 0;for (int i = 0; i < count; i++) {a +=5;}int b = 0;for (int i = 0; i < count; i++) {b--;}long time = System.currentTimeMillis() - start;System.out.println("count:" + formatToTenThousand(count) +", serial:" + time+"ms, b="+b+", a="+a);System.out.println();}private static String formatToTenThousand(int number) {double tenThousand = (double) number / 10000;DecimalFormat df = new DecimalFormat("0万");return df.format(tenThousand);}}

打印:

count:20000万, concurrency: 15ms,b=-200000000
count:20000万, serial:10ms, b=-200000000, a=1000000000

上述问题的答案是不一定,结果跟机器的核数相关,如果是1核处理器,那么肯定串行快,因为省略了创建线程和多线程上下文切换的时间,如果是多核,那么一般还是并发执行更快。而且这个代码示例中并发也只是两个线程,执行的结果意义不大。
我执行了多次也没有出现书中所描述的随着count数据量增大而出现并发执行更快的趋势,反而是串行一直都更快,即使count大小为两亿。而且这个代码示例基本没有io的处理,代表意义太有限。以我的经验来看,只是涉及在cpu中的计算,而没有与数据库等资源的交互,那么使用多线程意义不大。

1.1.2 如何减少上下文切换

减少上下文切换的方法有无锁并发编程、CAS算法、使用最少线程和使用协程。

  • 无锁并发编程。多线程竞争锁时,会引起上下文切换,所以多线程处理数据时,可以用一些办法来避免使用锁、如将数据的ID取余分段,不同的线程处理不同段的数据。
  • CAS算法。Java的java.util.concurrent.atomic包使用CAS算法来更新数据,而不需要加锁。
  • 使用最少线程。避免创建不需要的线程,比如任务很少,但是创建了很多线程来处理,这样会造成大量线程都处于等待状态。
  • 协程:在单线程里实现多任务的调度,并在单线程里维持多个任务间的切换。

java.util.concurrent.atomic包:
在这里插入图片描述

对于Java开发工程师而言,尽量使用JDK并发包提供的并发容器和工具类来解决并发问题,因为这些类都已经通过了充分的测试和优化

1.2 死锁

下面这段代码只是演示死锁的场景,在现实中你可能不会写出这样的代码。但是,在一些更为复杂的场景中,你可能会遇到这样的问题,比如t1拿到锁之后,因为一些异常情况没有释放锁(死循环)。又或者是t1拿到一个数据库锁,释放锁的时候抛出了异常,没释放掉。

public class DeadLockDemo {private static String A = "A";private static String B = "B";public static void main(String[] args) {new DeadLockDemo().deadLock();}private void deadLock() {Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {synchronized (A) {try {Thread.currentThread().sleep(2000L);} catch (InterruptedException e) {e.printStackTrace();}synchronized (B) {System.out.println("1");}}}});Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {synchronized (B) {try {Thread.currentThread().sleep(2000L);} catch (InterruptedException e) {e.printStackTrace();}synchronized (A) {System.out.println("2");}}}});t1.start();t2.start();}
}

避免死锁的几个常见方法:

  • 避免一个线程同时获取多个锁。
  • 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。
  • 尝试使用定时锁,使用lock.tryLock(timeout) 来替代使用内部锁机制。
  • 对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况。

1.3 资源限制的挑战

(1)什么是资源限制
资源限制就是程序的执行速度受限于计算机硬件或软件资源。
例如,服务器的带宽只有2Mb/s,每个资源的下载速度是1Mb/s,系统启动10个线程下载资源,下载速度不会变成10Mb/s,所以在进行并发编程时,要考虑这些资源的限制。硬件资源限制有带宽的上传/下载速度、硬盘读写速度和CPU的处理速度。软件资源限制有数据库的连接数和socket连接数等。

(2)资源限制引发的问题
在并发编程中,将代码执行速度加快的原则是将代码中串行执行的部分变成并发执行,但是如果将某段串行的代码并发执行,因为受限于资源,仍然在串行执行,这时候程序不仅不会加快速度,反而会更慢,因为增加了上下文切换和资源调度的时间。例如,之前看到一段程序使用多线程在办公网并发地下载和处理数据时,导致CPU利用率达到100%,几个小时都不能运行完成任务,后来修改为单线程,一个小时就执行完成了。

(3)如何解决资源限制的问题
对于硬件资源限制,可以考虑使用集群并行执行程序。既然单机的资源有限制,那么就让程序在多机上运行。比如使用ODPS、Hadoop或者自己搭建服务器集群,不同的机器处理不同的数据。可以通过“数据ID%机器数”,计算得到一个机器编号,然后由对应编号的机器处理这笔数据。
对于软件资源限制,可以考虑使用资源池将资源复用。比如使用连接池将数据库和socket连接复用,或者在调用对方webservice接口获取数据时,只建立一个连接。

WebService 与 Socket 区别

(4)在资源限制情况下进行并发编程
如何在资源限制的情况下,让程序执行得更快呢?方法就是,根据不同的资源限制调整程序的并发度,比如下载文件程序依赖于两个资源—带宽和硬盘读写速度。有数据库操作时,涉及数据库连接数,如果SQL语句执行非常快,而线程的数量比数据库连接数大很多,则某些线程会被阻塞,等待数据库连接。


读书笔记-Java并发编程的艺术

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/15714.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

探索大模型技术:AI的未来与挑战

随着人工智能技术的飞速发展&#xff0c;大模型技术已经成为推动AI领域进步的关键力量。在《中国人工智能系列白皮书——大模型技术&#xff08;2023 版&#xff09;》中&#xff0c;我们得以一窥这一领域的全貌&#xff0c;从技术演进到行业应用&#xff0c;再到伴随而来的安全…

C++拓展之scanf和printf

scanf和printf&#xff0c;这东西&#xff0c;说难也不难&#xff0c;可一旦深入学&#xff0c;学两天都可能学不完。 为了输入输出&#xff0c;我们要把这些占位符学一学。 我们来看看AI是怎么回答的。 Q&#xff1a;C格式化占位符有哪些&#xff1f; A&#xff1a;C中常用的…

【手把手带你搓组件库】从零开始实现Element Plus

从零开始实现Element Plus 前言亮点项目搭建1、创建项目初始化monorepo创建 .gitignore目录结构安装基础依赖配置文件创建各个分包入口utilscomponentscoreplaytheme 2、创建VitePress文档3、部署到Github Actions生成 GH_TOKENGitHub Page 演示 4、总结 前言 在本文中&#xf…

面向可复用性和可维护性的设计模式 课程学习总结

什么是设计模式 设计模式&#xff1a;在软件设计中给定上下文中常见问题的通用的、可重用的解决方案。 设计模式分类 1. 创建型模式——Creational patterns 关注对象创建的过程 1.1 工厂方法模式 定义用于创建对象的接口&#xff0c;但让子类决定要实例化哪个类。工厂方…

4.Redis之Redis的通用命令

0.Redis 实战操作 通过 redis-cli 客户端和 redis 服务器交互 涉及到很多的 redis 的命令 【redis 的命令非常非常多!!! 1.掌握常用命令(多操作多练习) 2.学会使用 redis 的文档-> 阅读文档, 是程序猿的基操!! redis 的命令非常非常多!!! 1.掌握常用命令(多操作多练习…

部署CNI网络组件+k8s多master集群部署+负载均衡

一、环境部署 主机服务 192.168.91.5 K8S集群master01192.168.91.8 K8S集群master02192.168.91.6K8S集群node01192.168.91.7K8S集群node02192.168.91.9 负载均衡nginxkeepalive01&#xff08;master&#xff09;192.168.91.10 负载均衡nginxkeepalive02&#xff08;backup&am…

编程实战:自己编写HTTP服务器(系列3:处理框架)

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 系列入口&#xff1a;编程实战…

TypeScript学习日志-第三十二天(infer关键字)

infer关键字 一、作用与使用 infer 的作用就是推导泛型参数&#xff0c;infer 声明只能出现在 extends 子语句中&#xff0c;使用如下&#xff1a; 可以看出 已经推导出类型是 User 了 二、协变 infer 的 协变会返回联合类型&#xff0c;如图&#xff1a; 三、逆变 infer…

pikachu靶场中的CSRF、SSRF通关

目录 1、CSRF介绍 2、CSRF&#xff08;get&#xff09; 3、CSRF&#xff08;post&#xff09; 4、CSRF Token 5、SSRF介绍 6、SSRF&#xff08;curl&#xff09; 7、SSRF&#xff08;file_get-content&#xff09; 8、CSRF与SSRF的区别 最近在学习CSRF、SSRF漏洞&#…

Haskell 的高阶函数(Higher-order functions)

本节继续介绍Haskell的知识&#xff0c;本节内容介绍的是高阶函数&#xff08;Higher-order functions&#xff09;的概念和应用。高阶函数是指能够接受其他函数作为参数&#xff0c;或者返回函数作为结果的函数。 Functions as values&#xff08;函数作为值&#xff09;&…

Boyer-Moore投票算法

摩尔投票法&#xff0c;又称为博耶-摩尔多数投票算法&#xff0c;是一种用于在一组数据中寻找多数元素(出现次数超过一半的元素)的算法。该算法的效率非常高&#xff0c;时间复杂度为O(n)&#xff0c;空间复杂度为O(1)&#xff0c;适合处理大数据量的情况。 步骤 首先定义两个…

飞机大战游戏实现揭秘

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、游戏概览与核心玩法 二、游戏模块详解 1. 游戏主循环模块 2. 创建初始化模块 三、关…

【pytorch】关于OpenCV和PIL.Image读取图片的区别

数据使用方角度 首先从pytorch出发&#xff0c;torchvision.transforms()要求传入的图像是PIL.Image格式&#xff08;通道要求是RGB格式的&#xff09;&#xff0c;另外模型处理输入要转换为[1,channel,H,W]&#xff1b; 所以最终导入torchvision.transforms()的图像格式需要转…

跟我学C++中级篇——内存屏障内存栅栏和编译器屏障以及相关

一、低级同步常见的技术术语 在一些操作系统或者计算机接口等比较原理化的书籍中&#xff0c;经常提到一些低级的同步术语&#xff0c;或者说一些同步的抽象的说法。最典型的就是内存内存屏障。不同的平台和语言有不同的叫法&#xff0c;有的叫内存栅栏或者屏障指令。它的主要…

在3090卡服务器上面进行funasr模型微调

文本记录了在3090卡上对实时asr模型进行微调的过程&#xff0c;包括数据准备、模型微调、验证微调后的模型。 一、参考文档&#xff1a; https://github.com/alibaba-damo-academy/FunASR/blob/main/examples/industrial_data_pretraining/paraformer_streaming/README_zh.md…

帝国CMS跳过选择会员类型直接注册方法

国CMS因允许多用户组注册&#xff0c;所以在注册页面会有一个选择注册用户组的界面&#xff0c;即使网站只用了一个用户组也会出现。 如果想去掉这个页面&#xff0c;直接进入注册页面&#xff0c;那么可按以下办法修改 打开 e/class/user.php 文件 查找&#xff1a; $chan…

TCP通信实现(服务端与客户端)

TCP通信实现&#xff08;服务器端) 案例 // TCP 通信的服务器端#include <stdio.h> #include <arpa/inet.h> #include <unistd.h> #include <string.h> #include <stdlib.h>int main() {// 1.创建socket(用于监听的套接字)int lfd socket(AF_…

Linux_CentOS7.6防火墙常用相关命令汇总(防火墙关闭与开启)

CentOS 防火墙是操作系统自带的一款网络安全工具&#xff0c;可以用于限制和管理网络流量。以下是一些 CentOS 防火墙相关的常用命令&#xff08;不常用的&#xff0c;太多了就不写了&#xff09;&#xff1a; 在centos7中用firewalld代替以前的iptables 一&#xff0c;查看防…

人脑与电脑有什么不同

人脑和计算机都是信息处理装置&#xff0c;目前人类对自己大脑的原理了解甚少。然而它们仍然有一些相似之处&#xff0c;例如都需要记忆来存储信息。人脑有些功能特性是计算机所没有的&#xff0c;且很难模拟出来&#xff0c;所以两者在原理上并不完全相同。 计算机由人类发明&…

美军配备人工智能武器的机器狗引发伦理争议

近日&#xff0c;美国海军陆战队特种作战司令部&#xff08;MARSOC&#xff09;的一项测试引发了全球关注&#xff1a;他们正在评估一种由“幽灵机器人”公司研发的最新型机器狗&#xff0c;并考虑为其配备“玛瑙工业”公司提供的武器系统。这一消息犹如在平静的湖面投下一颗石…