写点什么

深入理解 Java 多线程、Lambda 表达式及线程安全最佳实践

作者:小万哥
  • 2024-03-15
    广东
  • 本文字数:2782 字

    阅读完需:约 9 分钟

深入理解 Java 多线程、Lambda 表达式及线程安全最佳实践

Java 线程

线程使程序能够通过同时执行多个任务而更有效地运行。


线程可用于在不中断主程序的情况下在后台执行复杂的任务。

创建线程

有两种创建线程的方式。


  1. 扩展 Thread 类


可以通过扩展 Thread 类并覆盖其 run()方法来创建线程:


public class MyThread extends Thread {  public void run() {    System.out.println("This code is running in a thread");  }}
复制代码


  1. 实现 Runnable 接口


另一种创建线程的方式是实现 Runnable 接口:


public class MyRunnable implements Runnable {  public void run() {    System.out.println("This code is running in a thread");  }}
复制代码

运行线程

  1. 扩展 Thread 类


如果类扩展 Thread 类,则可以通过创建类的实例并调用其 start()方法来运行线程:


public class Main {  public static void main(String[] args) {    MyThread myThread = new MyThread();    myThread.start();    System.out.println("This code is outside of the thread");  }}
复制代码


  1. 实现 Runnable 接口


如果类实现了 Runnable 接口,则可以通过将类的实例传递给 Thread 对象的构造函数,然后调用线程的 start()方法来运行线程:


public class Main {  public static void main(String[] args) {    MyRunnable myRunnable = new MyRunnable();    Thread thread = new Thread(myRunnable);    thread.start();    System.out.println("This code is outside of the thread");  }}
复制代码

区分“扩展”和“实现”线程

主要区别在于,当一个类扩展 Thread 类时,无法扩展任何其他类,但通过实现 Runnable 接口,可以扩展另一个类,例如:


class MyClass extends OtherClass implements Runnable
复制代码

并发问题

因为线程与程序的其他部分同时运行,所以无法知道代码将按照什么顺序运行。当线程和主程序同时读取和写入相同的变量时,值是不可预测的。由此导致的问题称为并发问题。

示例

一个变量 amount 值不可预测的代码示例:


public class Main extends Thread {  public static int amount = 0;
public static void main(String[] args) { Main thread = new Main(); thread.start(); System.out.println(amount); amount++; System.out.println(amount); }
public void run() { amount++; }}
复制代码


为避免并发问题,最好尽可能少地在线程之间共享属性。如果需要共享属性,一种可能的解决方案是在使用线程可以更改的任何属性之前,使用线程的 isAlive()方法检查线程是否已完成运行。

示例

使用 isAlive()防止并发问题:


public class Main extends Thread {  public static int amount = 0;
public static void main(String[] args) { Main thread = new Main(); thread.start(); // 等待线程完成 while (thread.isAlive()) { System.out.println("Waiting..."); } // 更新amount并打印其值 System.out.println("Main program: " + amount); amount++; System.out.println("Main program: " + amount); }
public void run() { amount++; }}
复制代码

线程池

线程池是一种管理线程的资源。它允许您创建并维护一组可重用的线程。使用线程池可以提高应用程序的性能和效率。

线程安全

线程安全是指多个线程可以访问和修改数据而不导致数据损坏。使数据线程安全的一种方法是使用同步。同步是一种机制,它允许线程一次一个地访问共享数据。

常见的线程安全问题

  • 竞态条件:当多个线程同时访问共享数据并尝试对其进行更改时,就会发生竞态条件。这可能导致数据损坏。

  • 原子性:原子操作是指不可分割的操作。当多个线程尝试同时执行原子操作时,可能会导致数据损坏。

  • 可见性:当一个线程对共享数据进行更改时,其他线程必须能够看到这些更改。

避免线程安全问题

  • 使用同步

  • 使用不可变对象

  • 使用原子操作

Java Lambda 表达式

Lambda 表达式简介

Lambda 表达式是在 Java 8 中引入的。Lambda 表达式是一小段代码块,它接受参数并返回一个值。Lambda 表达式类似于方法,但它们不需要名称,并且可以直接在方法体中实现。

Lambda 表达式的语法

最简单的 Lambda 表达式包含一个参数和一个表达式:


参数 -> 表达式
复制代码


要使用多个参数,请将它们放在括号中:


(参数1, 参数2) -> 表达式
复制代码


表达式是有限制的。它们必须立即返回一个值,并且不能包含变量、赋值或 if 或 for 等语句。为了执行更复杂的操作,可以使用带有花括号的代码块。如果 Lambda 表达式需要返回一个值,那么代码块应该有一个 return 语句。


(参数1, 参数2) -> { 代码块 }
复制代码

Lambda 表达式的使用

Lambda 表达式通常作为参数传递给函数。在以下示例中,Lambda 表达式作为参数传递给 ArrayList 的 forEach()方法,以打印列表中的每个项:


import java.util.ArrayList;
public class Main { public static void main(String[] args) { ArrayList<Integer> numbers = new ArrayList<>(); numbers.add(5); numbers.add(9); numbers.add(8
); numbers.add(1); numbers.forEach((n) -> { System.out.println(n); }); }}
复制代码

Lambda 表达式的存储

如果变量的类型是仅具有一个方法的接口,那么 Lambda 表达式可以存储在变量中。Lambda 表达式应该具有与该方法相同数量的参数和相同的返回类型。Java 内置了许多这类接口,如 Consumer 接口(在 java.util 包中),它被列表使用。


import java.util.ArrayList;import java.util.function.Consumer;
public class Main { public static void main(String[] args) { ArrayList<Integer> numbers = new ArrayList<>(); numbers.add(5); numbers.add(9); numbers.add(8); numbers.add(1); Consumer<Integer> method = (n) -> { System.out.println(n); }; numbers.forEach(method); }}
复制代码

Lambda 表达式作为方法参数

要在方法中使用 Lambda 表达式,该方法应该有一个以单一方法接口作为其类型的参数。调用接口的方法将运行 Lambda 表达式。


interface StringFunction {  String run(String str);}
public class Main { public static void main(String[] args) { StringFunction exclaim = (s) -> s + "!"; StringFunction ask = (s) -> s + "?"; printFormatted("Hello", exclaim); printFormatted("Hello", ask); }
public static void printFormatted(String str, StringFunction format) { String result = format.run(str); System.out.println(result); }}
复制代码

Lambda 表达式的优势

  • 简化代码

  • 提高可读性

  • 增强代码的表达力


Lambda 表达式是 Java 8 中引入的一项强大功能,可以简化代码并提高可读性。它们是函数式编程的重要组成部分,可以用于各种任务,例如数据处理、事件处理和流处理。

最后

为了方便其他设备和平台的小伙伴观看往期文章:


微信公众号搜索:Let us Coding,关注后即可获取最新文章推送


看完如果觉得有帮助,欢迎 点赞、收藏、关注

发布于: 刚刚阅读数: 5
用户头像

小万哥

关注

代码如人生 2023-02-09 加入

编程爱好者

评论

发布
暂无评论
深入理解 Java 多线程、Lambda 表达式及线程安全最佳实践_Java_小万哥_InfoQ写作社区