JavaRush /Java 博客 /Random-ZH /喝咖啡休息#113。关于 Java 多线程,您可能不知道的 5 件事。解决技术债务问题的 10 个 JetBrai...

喝咖啡休息#113。关于 Java 多线程,您可能不知道的 5 件事。解决技术债务问题的 10 个 JetBrains 扩展

已在 Random-ZH 群组中发布

关于 Java 多线程您可能不知道的 5 件事

来源:DZone 线程是 Java 编程语言的核心。即使运行Hello World程序也需要主线程。如有必要,如果我们希望应用程序代码更具功能性和性能,则可以向程序添加其他线程。如果我们谈论的是 Web 服务器,那么它会同时处理数百个请求。为此使用多个线程。 喝咖啡休息#113。 关于 Java 多线程,您可能不知道的 5 件事。 解决技术债务问题的 10 个 JetBrains 扩展 - 1线程无疑很有用,但对于许多开发人员来说,使用它们可能很困难。在本文中,我将分享新手和经验丰富的开发人员可能不了解的五个多线程概念。

1、程序顺序和执行顺序不符

当我们编写代码时,我们假设它将完全按照我们编写的方式执行。然而,实际情况并非如此。如果 Java 编译器可以确定单线程代码中的输出不会改变,则可能会更改执行顺序以对其进行优化。看下面的代码片段:
package ca.bazlur.playground;

import java.util.concurrent.Phaser;

public class ExecutionOrderDemo {
    private static class A {
        int x = 0;
    }

    private static final A sharedData1 = new A();
    private static final A sharedData2 = new A();

    public static void main(String[] args) {
        var phaser = new Phaser(3);
        var t1 = new Thread(() -> {
            phaser.arriveAndAwaitAdvance();
            var l1 = sharedData1;
            var l2 = l1.x;
            var l3 = sharedData2;
            var l4 = l3.x;
            var l5 = l1.x;
            System.out.println("Thread 1: " + l2 + "," + l4 + "," + l5);
        });
        var t2 = new Thread(() -> {
            phaser.arriveAndAwaitAdvance();
            var l6 = sharedData1;
            l6.x = 3;
            System.out.println("Thread 2: " + l6.x);
        });
        t1.start();
        t2.start();
        phaser.arriveAndDeregister();
    }
}
这段代码看起来很简单。我们有两个使用两个线程的共享数据实例( sharedData1sharedData2 )。当我们执行代码时,我们期望输出如下:
线程 2: 3 线程 1: 0,0,0
但如果多次运行该代码,您将看到不同的结果:
线程 2: 3 线程 1: 3,0,3 线程 2: 3 线程 1: 0,0,3 线程 2: 3 线程 1: 3,3,3 线程 2: 3 线程 1: 0,3,0 线程 2 : 3 线程 1: 0,3,3
我并不是说所有这些流都将在您的计算机上完全像这样播放,但这完全有可能。

2、Java线程数量有限

在 Java 中创建线程很容易。然而,这并不意味着我们可以随心所欲地创建它们。线程的数量是有限的。我们可以使用以下程序轻松找出可以在特定机器上创建多少个线程:
package ca.bazlur.playground;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;

public class Playground {
    public static void main(String[] args) {
        var counter = new AtomicInteger();
        while (true) {
            new Thread(() -> {
                int count = counter.incrementAndGet();
                System.out.println("thread count = " + count);
                LockSupport.park();
            }).start();
        }
    }
}
上面的程序非常简单。它在循环中创建一个线程,然后将其停放,这意味着该线程将被禁用以供将来使用,但会执行系统调用并分配内存。程序继续创建线程,直到无法创建更多线程,然后抛出异常。我们感兴趣的是在程序抛出异常之前我们将收到的数字。在我的计算机上我只能创建 4065 个线程。

3.太多的线程并不能保证更好的性能

相信在 Java 中简化线程就能提高应用程序性能是天真的想法。不幸的是,这个假设对于我们今天 Java 提供的传统多线程模型来说是错误的。事实上,太多的线程会降低应用程序的性能。我们首先问这个问题:为了最大化应用程序性能,我们可以创建的最佳最大线程数是多少?嗯,答案并不那么简单。这在很大程度上取决于我们所做的工作类型。如果我们有多个独立的任务,所有这些任务都是计算性的并且不会阻塞任何外部资源,那么拥有大量线程并不会提高太多性能。另一方面,如果我们有一个 8 核处理器,则最佳线程数可能是 (8 + 1)。在这种情况下,我们可以依靠Java 8中引入的并行线程。默认情况下,并行线程使用共享的Fork/Join池。它创建的线程等于可用处理器的数量,这足以让它们集中工作。向没有任何阻塞的 CPU 密集型作业添加更多线程不会提高性能。相反,我们只会浪费资源。 笔记。拥有额外线程的原因是,即使是计算密集型线程有时也会导致页面错误或由于某些其他原因而被挂起。(参见:实践中的 Java 并行性,Brian Goetz,第 170 页)但是,例如,假设任务是 I/O 绑定的。在这种情况下,它们依赖于外部通信(例如数据库、其他 API),因此较大数量的线程是有意义的。原因是当一个线程正在等待 Rest API 时,其他线程可以继续工作。现在我们可以再问一次,对于这种情况,多少个线程才算太多呢?依靠。不存在适合所有情况的完美数字。因此,我们必须进行充分的测试,找出最适合我们特定工作负载和应用程序的方法。在最典型的场景中,我们通常有一组混合的任务。在这种情况下,事情就会完成。Brian Goetz 在他的《Java 并发实践》一书中提出了一个我们在大多数情况下都可以使用的公式。 线程数 = 可用核心数 * (1 + 等待时间 / 服务时间) 等待时间可以是 IO,比如等待 HTTP 响应、获取锁等。 服务时间(服务时间)是计算时间,例如处理 HTTP 响应、编组/解组等。例如,应用程序调用 API,然后对其进行处理。如果我们的应用服务器上有 8 个处理器,平均 API 响应时间为 100ms,响应处理时间为 20ms,那么理想的线程大小为:
N = 8 * ( 1 + 100/20) = 48
然而,这过于简单化了。充分的测试对于确定数量始终至关重要。

4.多线程不是并行

有时我们可以互换使用多线程和并行性,但这不再完全相关。尽管在 Java 中我们使用线程来实现这两者,但它们是两个不同的东西。“在编程中,多线程是一种特殊情况,无论进程如何运行,而并行性是同时执行(可能相关的)计算。多线程是指同时与许多事物进行交互。并发就是同时做很多事情。” Rob Pike 给出的上述定义是相当准确的。假设我们有完全独立的任务,并且它们可以单独计算。在这种情况下,这些任务称为并行任务,可以使用 Fork/Join 池或并行线程来执行。另一方面,如果我们有很多任务,其中一些任务可能会依赖于其他任务。我们的组合和构造方式称为多线程。和结构有关系。我们可能希望同时执行多项任务以获得某个结果,但不一定更快地完成一项任务。

5.Project Loom 允许我们创建数百万个线程

在上一点中,我认为拥有更多线程并不意味着提高应用程序性能。然而,在微服务时代,我们与太多的服务交互来完成任何特定的工作。在这种情况下,线程大部分时间都处于阻塞状态。虽然现代操作系统可以处理数百万个打开的套接字,但我们无法打开许多通信通道,因为我们受到线程数量的限制。但是,如果您创建数百万个线程,并且每个线程都使用一个开放的套接字与外界通信,该怎么办?这肯定会提高我们的应用程序吞吐量。为了支持这个想法,Java 中有一个名为 Project Loom 的计划。使用它,我们可以创建数百万个虚拟线程。例如,使用以下代码片段,我能够在我的计算机上创建 450 万个线程。
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;

public class Main {
    public static void main(String[] args) {
        var counter = new AtomicInteger();

        // 4_576_279
        while (true) {
            Thread.startVirtualThread(() -> {
                int count = counter.incrementAndGet();
                System.out.println("thread count = " + count);
                LockSupport.park();
            });
        }
    }
}
要运行此程序,您必须安装 Java 18,可以在此处下载。您可以使用以下命令运行代码: java --source 18 --enable-preview Main.java

解决技术债务问题的 10 个 JetBrains 扩展

来源:DZone 许多开发团队都感受到按时完成任务的巨大压力。因此,他们通常没有足够的时间来修复和清理代码库。有时在这些情况下,技术债务会迅速积累。编辑器扩展可以帮助解决这个问题。让我们看一下 10 个解决技术债务的最佳 JetBrains 扩展(支持 Java)。 喝咖啡休息#113。 关于 Java 多线程,您可能不知道的 5 件事。 解决技术债务的 10 个 JetBrains 扩展 - 2

重构和技术债务工具

1.重构洞察

RefactorInsight 通过提供有关重构的信息来提高 IDE 中代码更改的可见性。
  1. 该扩展定义了合并请求中的重构。
  2. 标记包含重构的提交。
  3. 帮助查看在 Git 日志选项卡中选择的任何特定提交的重构。
  4. 显示类、方法和字段的重构历史。

2. IDE 中的 Stepsize 问题跟踪器

Stepsize 对于开发人员来说是一个很棒的问题跟踪器。该扩展不仅可以帮助工程师创建更好的 TODO 和代码注释,还可以优先考虑技术债务、重构等:
  1. Stepsize 允许您直接在编辑器中创建和查看代码中的任务。
  2. 查找影响您正在开发的功能的问题。
  3. 使用 Jira、Asana、Linear、Azure DevOps 和 GitHub 集成将问题添加到冲刺中。

3.新的Relic CodeStream

New Relic CodeStream 是一个用于讨论和审查代码的开发人员协作平台。它支持来自 GitHub、BitBucket 和 GitLab 的拉取请求,支持来自 Jira、Trello、Asana 和其他 9 个平台的问题管理,并提供代码讨论,将所有这些结合在一起。
  1. 在 GitHub 中创建、审查和合并拉取请求。
  2. 通过初步代码审查获取正在进行的工作的反馈。
  3. 与队友讨论代码问题。

TODO 和评论

4.评论荧光笔

该插件允许您创建注释行和语言关键字的自定义突出显示。该插件还能够定义自定义标记以突出显示注释行。

5.更好的评论

Better Comments 扩展可帮助您在代码中创建更清晰的注释。通过此扩展,您将能够将注释分类为:
  1. 警报。
  2. 要求。
  3. 去做。
  4. 基本时刻。

错误和安全漏洞

6.SonarLint_ _

SonarLint 允许您在代码问题出现之前对其进行故障排除。它也可以用作拼写检查器。SonarLint 在您编码时突出显示错误和安全漏洞,并提供明确的修复说明,以便您可以在提交代码之前修复它们。

7.斑点虫子

SpotBugs 插件提供静态字节码分析,以查找 IntelliJ IDEA 中 Java 代码中的错误。SpotBugs 是一种 Java 缺陷检测工具,它使用静态分析来查找 400 多种错误模式,例如空指针取消引用、无限递归循环、Java 库的滥用和死锁。SpotBugs 可以识别大型应用程序中的数百个严重缺陷(通常每 1000-2000 行未注释的原始语句大约有 1 个缺陷)。

8.Snyk漏洞扫描器

Snyk 的漏洞扫描程序可帮助您查找并修复项目中的安全漏洞和代码质量问题。
  1. 查找并修复安全问题。
  2. 查看按类别划分的不同类型问题的列表。
  3. 显示故障排除提示。

9.零宽度字符定位器

该插件增强了对源代码和资源中与不可见零宽度字符相关的难以发现的错误的检查和检测。使用时,请确保启用“零宽度unicode字符”检查。

10.代码M ​​R_

CodeMR是一款软件质量和静态代码分析工具,可以帮助软件公司开发更好的代码和程序。CodeMR 在各种视图(例如包结构、TreeMap、Sunburst、依赖关系和图形视图)中可视化代码度量和高级质量属性(耦合、复杂性、内聚性和大小)。
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION