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

Java 线程

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

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

创建线程

有两种创建线程的方式。

  1. 扩展Thread类

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

1public class MyThread extends Thread { 2 public void run() { 3 System.out.println("This code is running in a thread"); 4 } 5}
  1. 实现Runnable接口

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

1public class MyRunnable implements Runnable { 2 public void run() { 3 System.out.println("This code is running in a thread"); 4 } 5}

运行线程

  1. 扩展Thread类

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

1public class Main { 2 public static void main(String[] args) { 3 MyThread myThread = new MyThread(); 4 myThread.start(); 5 System.out.println("This code is outside of the thread"); 6 } 7}
  1. 实现Runnable接口

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

1public class Main { 2 public static void main(String[] args) { 3 MyRunnable myRunnable = new MyRunnable(); 4 Thread thread = new Thread(myRunnable); 5 thread.start(); 6 System.out.println("This code is outside of the thread"); 7 } 8}

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

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

1class MyClass extends OtherClass implements Runnable

并发问题

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

示例

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

1public class Main extends Thread { 2 public static int amount = 0; 3 4 public static void main(String[] args) { 5 Main thread = new Main(); 6 thread.start(); 7 System.out.println(amount); 8 amount++; 9 System.out.println(amount); 10 } 11 12 public void run() { 13 amount++; 14 } 15}

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

示例

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

1public class Main extends Thread { 2 public static int amount = 0; 3 4 public static void main(String[] args) { 5 Main thread = new Main(); 6 thread.start(); 7 // 等待线程完成 8 while (thread.isAlive()) { 9 System.out.println("Waiting..."); 10 } 11 // 更新amount并打印其值 12 System.out.println("Main program: " + amount); 13 amount++; 14 System.out.println("Main program: " + amount); 15 } 16 17 public void run() { 18 amount++; 19 } 20}

线程池

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

线程安全

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

常见的线程安全问题

  • 竞态条件:当多个线程同时访问共享数据并尝试对其进行更改时,就会发生竞态条件。这可能导致数据损坏。
  • 原子性:原子操作是指不可分割的操作。当多个线程尝试同时执行原子操作时,可能会导致数据损坏。
  • 可见性:当一个线程对共享数据进行更改时,其他线程必须能够看到这些更改。

避免线程安全问题

  • 使用同步
  • 使用不可变对象
  • 使用原子操作

Java Lambda表达式

Lambda表达式简介

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

Lambda表达式的语法

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

1参数 -> 表达式

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

1(参数1, 参数2) -> 表达式

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

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

Lambda表达式的使用

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

1import java.util.ArrayList; 2 3public class Main { 4 public static void main(String[] args) { 5 ArrayList<Integer> numbers = new ArrayList<>(); 6 numbers.add(5); 7 numbers.add(9); 8 numbers.add(8 9 10); 11 numbers.add(1); 12 numbers.forEach((n) -> { System.out.println(n); }); 13 } 14}

Lambda表达式的存储

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

1import java.util.ArrayList; 2import java.util.function.Consumer; 3 4public class Main { 5 public static void main(String[] args) { 6 ArrayList<Integer> numbers = new ArrayList<>(); 7 numbers.add(5); 8 numbers.add(9); 9 numbers.add(8); 10 numbers.add(1); 11 Consumer<Integer> method = (n) -> { System.out.println(n); }; 12 numbers.forEach(method); 13 } 14}

Lambda表达式作为方法参数

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

1interface StringFunction { 2 String run(String str); 3} 4 5public class Main { 6 public static void main(String[] args) { 7 StringFunction exclaim = (s) -> s + "!"; 8 StringFunction ask = (s) -> s + "?"; 9 printFormatted("Hello", exclaim); 10 printFormatted("Hello", ask); 11 } 12 13 public static void printFormatted(String str, StringFunction format) { 14 String result = format.run(str); 15 System.out.println(result); 16 } 17}

Lambda表达式的优势

  • 简化代码
  • 提高可读性
  • 增强代码的表达力

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

最后

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

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

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

点赞
收藏

评论区

加载中...

相关推荐

Java学习 Day01 多线程

Java学习Day01多线程Java多线程实现方式有2种1.继承Thread类,重写run方法案例测试类Thread01Javapackagetop.wanqq.thread;/@authorwanqq/publicclassThread01extendsThread@Overridepubli

java 多线程

创建线程的4种方式1、继承Thread类,复写run方法,run方法中为线程需要执行的逻辑部分,而启动线程调用start方法。小示例见代码,通过Thread.currentThread().getName()可以获得当前线程名称publicclassMyThreadextendsThread{private

java多线程和异步回调

   在实际开发过程中遇到的多线程情况不多,但是在生产环境中多线程是最基本的情况,java面试时也会考到,所以看看多线程的知识还是很有必要的。 Thread,Runnable,Callable,Future,FutureTask,Executors这是java常见的接口和类。  thread.run():线程具体要执行的代码,thread.jo

java线程

1.进程和线程的区别是什么?进程是执行着的应用程序,而线程是进程内部的一个执行序列。一个进程可以有多个线程。线程又叫轻量级进程。2.创建线程有几种不同方式?你喜欢哪种?为什么?有三种方式可以用来创建线程:继承Thread类实现Runnable接口应用程序可以使用Executor框架来创建线程池实现Runnabl

java创建线程的三种方式及其对比

Java(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Flib.csdn.net%2Fbase%2F17)中创建线程主要有三种方式:一、继承Thread类创建线程类(1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run(

一篇文章弄懂Java多线程基础和Java内存模型

文章目录一、多线程的生命周期及五种基本状态二、Java多线程的创建及启动1.继承Thread类,重写该类的run()方法2.通过实现Runnable接口创建线程类3.通过Callable和Future接口创建线程三、Java内存模型概念四、内存间的交互操作五、volatile和synchronized的