首页 > 编程语言 >【Java技术专题】「入门到精通系列教程」深入探索Java特性中并发编程体系的原理和实战开发指南( 线程基础技术专题)

【Java技术专题】「入门到精通系列教程」深入探索Java特性中并发编程体系的原理和实战开发指南( 线程基础技术专题)

时间:2023-08-18 10:01:43浏览次数:39  
标签:专题 Java synchronized 编程 并发 线程 public

并发编程介绍

当今软件开发领域越来越强调性能和伸缩性,因此,利用并发编程可以更好地利用计算资源,提高应用程序的性能和响应速度。以下是一些关于并发编程的相关信息供您参考。

什么是并发编程

并发编程是指在一个程序中同时执行多个独立的计算任务,并通过各种手段来协调不同任务之间的交互,从而提高程序的效率和可扩展性。

并发编程的好处是什么

并发编程可以使程序可以同时利用多个CPU核心执行不同的任务,从而提升程序的效率和性能。此外,使用并发编程可以简化需要相互协调的任务之间的通信和同步,减少系统的复杂度和出错的风险。

并发编程的挑战是什么

并发编程面临许多挑战,如资源竞争、死锁、饥饿和活锁等问题。同时,在并发系统中,程序的错误也可能导致数据不一致和安全漏洞等问题。因此,在设计和开发并发系统时需要仔细考虑各种问题。

并发编程模型有哪些

常见的并发编程模型包括多线程模型、多进程模型和协程模型。每种模型都有自己的特点,相应的编程语言和框架也提供了支持。

如何学习并发编程

学习并发编程需要掌握并发编程的基本理论,同时需要熟悉各种并发编程模型和工具。建议初学者先学习基础的多线程编程和锁机制,逐步学习更高级的并发编程模型和相关技术,如Actor模型、RxJava、Go语言等。另外,实践也是学习并发编程的关键,可以通过实际编写并发程序来获得经验和技能。

本系列专题文章大全

【Java技术专题】「入门到精通系列教程」深入探索Java特性中并发编程体系的原理和实战开发指南( 线程基础技术专题)_Java

实战原理

计算的问题

如何更快地计算一个包含亿级 Integer 类型数值的 List 的总和?

【Java技术专题】「入门到精通系列教程」深入探索Java特性中并发编程体系的原理和实战开发指南( 线程基础技术专题)_Java_02

简单的方法:更快的CPU来遍历

单线程处理大数据量的数据集合的分析是一项挑战性的任务,因为单线程处理大数据量的数据集合可能会导致程序的运行速度变慢,甚至崩溃。伴随着数据量越来越大,耗费的时间约会越来越长,如下图所示。

【Java技术专题】「入门到精通系列教程」深入探索Java特性中并发编程体系的原理和实战开发指南( 线程基础技术专题)_Java_03

靠谱的方法:分而治之来处理

在对性能的追求很可能是并发bug唯一最大的来源!同样不是免费的午餐,需要学习和批量实践。

【Java技术专题】「入门到精通系列教程」深入探索Java特性中并发编程体系的原理和实战开发指南( 线程基础技术专题)_Java_04

进一步的办法:Fork/Join技术

Fork/Join是一种并行编程技术,常用于解决分治问题。具体来说,它将一个大问题分成若干个小问题,递归地将小问题分解成更小的子问题,直到问题的规模足够小可以被直接解决。然后将解决子问题的结果合并起来,得到大问题的解。Fork/Join使用了线程池的概念,并且自动运用一些启发式策略优化任务调度,从而保证任务的高效执行。Fork/Join 技术在并行计算中发挥着重要作用,能够提高多线程执行效率和程序处理大规模数据的能力。

【Java技术专题】「入门到精通系列教程」深入探索Java特性中并发编程体系的原理和实战开发指南( 线程基础技术专题)_可见性_05

在这个模式中,原始任务被递归地分解成更小的子任务,直到这些任务足够小,可以被直接处理。每个子任务都是在一个单独的线程中运行的,然后将它们的结果合并在一起,直到最终产生原始任务的结果。这种方式可以很好地利用多核CPU,从而提高性能。

线程是并发编程的基础

在Java中,线程技术是支持并发编程的基础。并发编程是指多个线程同时执行任务,它能够充分利用计算机的多核处理器,提高程序的执行效率和响应速度。

线程

线程:是实现多任务并发执行的关键。在Java中,我们可以使用多线程技术来同时执行多个任务,提高程序的并发性和吞吐量。因此,Java中的线程技术是并发编程的基础。

【Java技术专题】「入门到精通系列教程」深入探索Java特性中并发编程体系的原理和实战开发指南( 线程基础技术专题)_可见性_06

  • 多线程并发中:如果修改了共享变量的值,必须将其同步回主内存,其他线程才能访问到。
  • 为了保证内存访问的顺序,可以使用Java提供的同步机制或volatile关键字。
  • Cache coherency指管理多处理器系统的高速缓存区结构,确保数据在高速缓存区到内存的传输中不会丢失或重复。

技术选型

Java 中的 happens-before ordering 通过一些关键字和类提供了可靠的内存可见性和线程同步机制,这些关键字和类包括:synchronized、volatile、final、java.util.concurrent.lock|atomic。

内存中的可见部分

【Java技术专题】「入门到精通系列教程」深入探索Java特性中并发编程体系的原理和实战开发指南( 线程基础技术专题)_可见性_07

当多个线程访问共享资源时,可能会出现数据不一致的问题,这时就需要使用同步机制来解决。因此JMM推出了三个特性:可见性、有序性、原子性。

【Java技术专题】「入门到精通系列教程」深入探索Java特性中并发编程体系的原理和实战开发指南( 线程基础技术专题)_并发编程_08

synchronized同步机制

synchronized 是 Java 中最基本的同步技术,它可以在多个线程之间提供原子性和可见性,保证一段代码在同一时刻只有一个线程可以执行。

保证原子性和可见性

【Java技术专题】「入门到精通系列教程」深入探索Java特性中并发编程体系的原理和实战开发指南( 线程基础技术专题)_可见性_09

使用 synchronized 时,需要指定一个锁对象,在任意时刻,最多只有一个线程可以拥有该锁对象,从而保证同一时刻只能有一个线程执行被锁定的代码块。

锁的类型和范围

synchronized 可以用来修饰普通方法、静态方法和代码块,以适应不同场景的同步需求。

【Java技术专题】「入门到精通系列教程」深入探索Java特性中并发编程体系的原理和实战开发指南( 线程基础技术专题)_并发编程_10

线程的Java Monitors监视器

在Java中,每个对象都有一个内部的锁,也称为对象监视器(monitor)。synchronized 关键字就是用来获得和释放对象的监视器的。当一个线程获得了一个对象的锁,并进入了该对象的同步代码块,其他线程如果需要获取该对象的锁就会被阻塞,直到该线程释放锁为止。因此,synchronized 关键字是实现线程同步的重要手段之一。在 Java 中,每个对象都有一个监视器锁(也称为内置锁或反应锁),它可用于实现同步。

synchronized 就是通过锁来实现同步的,当一个线程获取到了锁,其他需要访问被锁住的代码块的线程会被阻塞,等待锁被释放。同时,每个对象只有一个锁,一个线程对该对象的锁拥有之后,其他访问该对象的线程将无法操作该对象的所有同步方法和同步代码块,这就能够保证了多个线程在访问共享资源时的同步。因此,对象监视器和 synchronized 锁是密不可分的。

【Java技术专题】「入门到精通系列教程」深入探索Java特性中并发编程体系的原理和实战开发指南( 线程基础技术专题)_并发编程_11

该图将监视器显示为三个矩形。在中心,一个大矩形包含一个线程,即监视器的所有者。在左侧,一个小矩形包含条目集。在右边,另一个小矩形包含等待集。活动线程显示为深灰色圆圈。悬挂的线程用浅灰色的圆圈表示。

线程的可见性
volatile关键字
  1. 简化实现或者同步策略验证的时候来使用它;
  2. 确保引用对象的可见性;
  3. 标示重要的生命周期的事件,例如:开始或者关闭。
脆弱的volatile的使用条件:
  1. 写入变量不依赖变量的当前值,或者能够保证只有单一的线程修改变量的值;
  2. 变量不需要和其他变量共同参与不变约束;
  3. 访问变量时不需要其他原因需要加锁。
private volatile boolean isInterrupted = false;

【Java技术专题】「入门到精通系列教程」深入探索Java特性中并发编程体系的原理和实战开发指南( 线程基础技术专题)_可见性_12

线程:独占锁

非方法修饰符和类修饰符可以用来修饰 Java 中的各种程序元素,需要注意的是,在方法覆盖时需要添加 synchronized 修饰符。

关于经典的顺序锁问题,则是在多线程环境下,如果将两个线程安全的方法组合在一起,不一定能够保证整体的线程安全性。

另外还有一个常见的问题就是,在 Java 中当一个对象调用 getClass() 方法时,该方法会返回该对象所属的类的 Class 对象。

Object a new object();
Object b new object();
public void order(){
	synchronized(a){
	//do something
		synchronized(b){
			//do something
		}
	}
}
public void order1(){
	synchronized(b){
		//do something
		synchronized(a){
			//do something
		}
	}
}
线程:分拆锁

在多线程编程中,为了保证数据的正确性和避免竞态条件,我们通常会使用锁来对临界区进行保护。然而,在某些情况下,线程之间的锁竞争可能会导致性能瓶颈,造成程序的性能下降。

public class ServerStatus{
public final Set<String>users new HashSet<String>();
public final Set<String>queries new HashSet<String>();
public synchronized void adduser(String u){users.add(u);
public synchronized void addQuery(String q){queries.add(q);
public synchronized void removeUser(String u)(users.remove (u);}
public synchronized void removeQuery(String q)(queries.remove(q);
}

在这种情况下,我们可以尝试将锁拆成多个细粒度的部分,分别对每个细粒度部分进行锁定,并发执行,尽量减小锁竞争范围,以提高程序的并发性和性能。这种技术就被称为“线程分拆锁”。

public class SpinOff Server Status public final Set<String>users=new HashSet<String>() ;public void addUser(String u){
synchronized(users) {
users.add(u) ;
}
public void remove User(String u){
synchronized(users) {
users.remove(u) ;
}
}

例如,如果在一个大的数据结构上进行操作,我们可以将数据结构划分为多个小的数据块,对每个数据块进行单独的锁控制和并发访问。这样可以更细粒度地控制锁的范围,减小锁的竞争,提高并发性能。

需要注意的是,在使用线程分拆锁的技术时,要确保各个锁的操作是互不干扰、没有依赖关系的,否则可能会导致死锁或者其他并发问题。此外,线程分拆锁一般需要对数据结构进行重新设计和调整,需要仔细评估和测试,以确保其可行性和有效性。

线程:分离锁
private final Object[]locks;
private static final in tN_LOCKS=4;
private final String[] share;
private into pNum;
private in tN_A NUM;
public Stripping Lock(int on,int a num) {
	opNum=on;
	N_ANUM=anum;
	share=new String[NANUM];
	locks=new Object[NLOCKS];
	for(int i=0;i<NLOCKS; i++)
		locks[i] =new Object();
}

public synchronized void put1(int indx,String k)
	share[indx]=k;//acquire the object lock
}
public void put 2(int indx, String k){
    synchronized(locks[indx号N_LOCKS] ) {
    	share[indx] =k; //acquire the corresponding lock 1
	}
}

分离锁存在的缺点之一是,对容器加锁可能会导致独占访问变得更加困难和昂贵。此外,使用分离锁可能会导致内存使用过多,例如在Action层使用ConcurrentHashMap曾因此问题导致内存使用过大,修改array后竟然单台服务器节省2G。

教父Joshua Bloch说线程

【Java技术专题】「入门到精通系列教程」深入探索Java特性中并发编程体系的原理和实战开发指南( 线程基础技术专题)_Java_13

书籍推荐

【Java技术专题】「入门到精通系列教程」深入探索Java特性中并发编程体系的原理和实战开发指南( 线程基础技术专题)_可见性_14

【Java技术专题】「入门到精通系列教程」深入探索Java特性中并发编程体系的原理和实战开发指南( 线程基础技术专题)_Java_15

【Java技术专题】「入门到精通系列教程」深入探索Java特性中并发编程体系的原理和实战开发指南( 线程基础技术专题)_可见性_16

【Java技术专题】「入门到精通系列教程」深入探索Java特性中并发编程体系的原理和实战开发指南( 线程基础技术专题)_Java_17

标签:专题,Java,synchronized,编程,并发,线程,public
From: https://blog.51cto.com/alex4dream/7131978

相关文章

  • C++ 多线程详解之异步编程 std::packaged_task
    std::packaged_task将任何可调用对象(比如函数、lambda表达式等等)封装成一个task,可以异步执行。执行结果可以使用std::future获取。比如下面的例子,构造一个std::packaged_task后,get_future()函数返回一个std::future对象,可以获取task异步或者同步执行的结果。#includ......
  • QtWebChannel和JavaScript进行通信(简单理解)
    说明在使用Qt(C++)和JavaScript之间实现通信时,通常会使用一些模块和技术来使两者能够交互和传递数据。这种通信通常用于在Qt应用程序中嵌入Web内容,或者在Web页面中嵌入Qt应用程序。以下是一些常用的模块和技术,以及它们的作用QtWebEngine模块:作用:QtWebEngine是Qt中的Web引擎,允......
  • Java 子父类型集合之间的转换
    假设现在有这样一个方法,入参是父类型的集合参数,这是个通用方法,你需要共用它,你现在要传子类型集合进去,怎么办?classAnimal{}classDogextendsAnimal{}publicclassService{publicvoiddoSth(){List<Animal>animals=newArrayList<>();han......
  • java 每天23点定时删除某个Folder下的文件
    importjava.io.IOException;importjava.nio.file.*;importjava.time.LocalDate;importjava.time.LocalDateTime;importjava.time.LocalTime;importjava.util.concurrent.Executors;importjava.util.concurrent.ScheduledExecutorService;importjava.util.concu......
  • Java日志系列:日志门面JCL、SLF4J
    目录一、日志门面说明二、JCL使用JCL入门JCL原理三、SLF4J使用配合自身简单日志实现(slf4j-simple)配置logback日志实现配置Log4J日志实现(需适配器)配置JUL日志实现(需适配器)添加slf4j-nop依赖(日志开关)桥接旧的日志实现框架一、日志门面说明当我们的系统变的更加复杂的......
  • 线程与进程的区别
    线程与进程的区别?线程是指进程内的一个执行单元,也是进程内的可调度实体.与进程的区别:地址空间:进程内的一个执行单元;进程至少有一个线程;它们共享进程的地址空间;而进程有自己独立的地址空间;资源拥有:进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源线程......
  • 【愚公系列】2023年08月 WPF控件专题 Label、TextBox、PasswordBox控件介绍
    (文章目录)前言WPF控件是WindowsPresentationFoundation(WPF)中的基本用户界面元素。它们是可视化对象,可以用来创建各种用户界面。WPF控件可以分为两类:原生控件和自定义控件。原生控件是由Microsoft提供的内置控件,如Button、TextBox、Label、ComboBox等。这些控件都是WPF中常见......
  • 老杜 JavaWeb 讲解(二十一)——通过银行账户转账业务讲解MVC架构
    老杜-通过银行账户转账业务讲解MVC架构老杜-银行账户转账(mvc001)这个项目将层层迭代,最终成为MVC架构的项目。老杜第一次写代码并没有使用JDBC的封装类,但大差不差,这里即使用了之前的DBUtil.java,代码依然很杂乱。建立数据库数据库名:mvc字符集:utf8mb4排序规则:utf8mb4_unicod......
  • 在Java中print//printf//和println的区别
    1.printprint在JAVA中常常使用System.out.pirnt();的输出格式。在Java中进行一般的输出语句。例子如下: 输出 可见其不会换行。2.printfprintf在JAVA中常常使用System.out.printf();的格式。在Java中printf常用于格式转换,但需要注意不是换行输出,只用于精度转换。例子如......
  • JAVA 找不到主類
    检查代码和项目结构首先,你需要检查你的代码和项目结构是否正确。确保你的代码中有一个包含main方法的主类,并且这个类的包路径和类名都是正确的。 检查Classpath路径Classpath是用来告诉Java虚拟机(JVM)在哪里寻找需要的类文件的路径。如果你的程序依赖了一些外部的类库或者其......