Java 并发库

All lectures for ZH purposes
第 1 级, 课程 1044
可用

Java 中的多线程

Java 虚拟机支持并行计算。所有计算都可以在一个或多个线程的上下文中执行。我们可以轻松地为多个线程设置对同一资源或对象的访问,以及设置一个线程来执行单个代码块。

对于分配有多个线程的资源,任何开发人员都需要在读取和写入操作期间与线程同步工作。

重要的是,在访问资源时,您拥有最新的数据,以便另一个线程可以更改它并获得最新的信息。即使我们以银行账户为例,在钱到账之前,您无法使用它,因此始终拥有最新数据很重要。Java 具有用于同步和管理线程的特殊类。

线程对象

这一切都从主(main)线程开始,也就是说,至少你的程序已经有一个正在运行的线程。主线程可以使用Callable或Runnable创建其他线程。创建仅在返回结果上有所不同,Runnable不返回结果并且不能抛出已检查的异常。因此,您获得了一个利用文件构建高效工作的好机会,但这是非常危险的,您需要小心。

也可以在单独的 CPU 核心上安排线程执行。系统可以轻松地在线程之间移动并通过正确的设置执行特定的线程:即读取数据的线程首先执行,一旦我们有数据,然后我们将其传递给负责验证的线程,之后我们将它传递给线程以执行一些业务逻辑,然后一个新线程将它们写回。在这种情况下,4 个线程轮流处理数据,一切都会比一个线程更快。每个这样的流都被转换为本机 OS 流,但如何转换取决于 JVM 实现。

Thread类用于创建和使用线程。它有标准的控制机制,也有抽象的机制,例如java.util.concurrent中的类和集合。

Java中的线程同步

通过共享对对象的访问来提供通信。这样很有效,但同时工作时也很容易出错。错误有两种情况:线程干扰 - 当另一个线程干扰您的线程时,以及内存一致性错误 - 内存一致性。为了解决和防止这些错误,我们有不同的同步方法。

Java 中的线程同步由监视器处理,这是一种高级机制,一次只允许一个线程执行受同一监视器保护的代码块。监视器的行为是根据锁来考虑的;一台显示器 - 一把锁。

同步有几个要点需要注意。第一点是互斥——只有一个线程可以拥有监视器,因此监视器上的同步意味着一旦一个线程进入监视器保护的同步块,其他线程就不能进入监视器保护的块。这个监视器直到第一个线程退出同步块。即多个线程不能同时访问同一个同步块。

但同步不仅仅是互斥。同步确保在同步块之前或同步块内写入内存的数据对同一监视器上同步的其他线程可见。退出块后,我们释放监视器,另一个线程可以抓住它并开始执行这段代码。

当一个新线程捕获监视器时,我们可以访问并执行该代码块,并且在那个时间点将从主内存加载变量。然后我们可以看到之前版本的监视器显示的所有条目。

如果字段被声明为volatile(这对long和double很重要),或者对它的所有访问都受同一个锁保护,那么对该字段的单次读或写是原子执行的。如果没有同步,就可能出现重排序(reordering)效应:在未正确同步的程序中,一个线程可能会以意想不到的顺序看到另一个线程所做的修改。

线程的互斥和同步的作用,即只有进入隐式获取锁的synchronized块或方法,或者显式获取锁,才能实现它们的正确运行。我们将在下面讨论它。这两种工作方式都会影响您的记忆力,重要的是不要忘记使用volatile变量。

Java 中的可变字段

关键字volatile解决的是可见性问题。如果一个线程向volatile字段写入了值,那么之后读取该字段的任何其他线程都会看到新值,而不是过时的副本。

就对内存的效果而言,写入volatile字段类似于释放监视器,而读取则类似于获取监视器。线程 A 在写入volatile字段之前写入的所有内容,只要线程 B 从该字段读到了这个写入的值,线程 B 就保证能看到。这种关系称为 happens-before(“先行发生”)。

对volatile字段的单次读或写是原子的,包括long和double在内。但像count++(读取、递增、写入)这样的复合操作则不是:在各步骤之间可能有另一个线程插入,部分递增就会丢失。volatile不提供互斥。要让复合操作以原子方式执行,需要使用synchronized、Lock或AtomicInteger、AtomicLong等类。

Java并发

如果你想制作一个超级高效的多线程应用程序,你必须使用JavaConcurrent库中的类,它们位于java.util.concurrent包中。

该库非常庞大,功能各不相同,所以让我们看一下里面的内容并将其分为一些模块:

Java并发

并发集合是一组用于在多线程环境中工作的集合。与阻止访问整个集合的基本包装器 Collections.synchronizedList 不同,锁用于数据段或无等待算法用于并行读取数据。

队列- 用于在多线程环境中工作的非阻塞和阻塞队列。非阻塞队列专注于速度和操作而不阻塞线程。当您需要“减慢” Producer或Consumer线程时,阻塞队列适合工作。比如在某些条件不满足的情况下,队列为空或者满,或者没有空闲的Consumer'a。

同步器是用于同步线程的实用工具。它们是“并行”计算的有力武器。

Executors是一个更方便和容易创建线程池的框架,很容易设置异步任务的调度和获取结果。

锁相对于基本的synchronized、 wait、 notify、 notifyAll ,是很多灵活的线程同步机制。

原子是可以支持对基元和引用的原子操作的类。

评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION