首页 > 编程语言 >深入理解 Java 多线程、Lambda 表达式及线程安全最佳实践

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

时间:2024-03-15 22:24:05浏览次数:23  
标签:Main Java void 线程 多线程 public 表达式 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,关注后即可获取最新文章推送

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

标签:Main,Java,void,线程,多线程,public,表达式,Lambda
From: https://www.cnblogs.com/xiaowange/p/18076372

相关文章

  • Windows 部署 JAVA服务
    WinSW.exe是一个可以将Windows上的任何一个程序注册为服务的工具,也可以进行卸载该服务。下载WinSW.exe重命名为 bsq-admin.exe,放到 C:\bsq\bsqmes文件夹复制bsq-admin.jar包到 C:\bsq\bsqmes文件夹新建 bsq-admin.xml配置文件,内容如下:<service>   <id>bsq-......
  • L1-7 分寝室 [java]
    分数20学校新建了宿舍楼,共有 n 间寝室。等待分配的学生中,有女生 n0​ 位、男生 n1​ 位。所有待分配的学生都必须分到一间寝室。所有的寝室都要分出去,最后不能有寝室留空。现请你写程序完成寝室的自动分配。分配规则如下:男女生不能混住;不允许单人住一间寝室;对每种性......
  • 什么是分库分表?用Java手写一个分库分表组件
    分库分表分库分表路由组件的主要功能是负责处理数据在多个数据库和表之间的分配和路由。在分库分表的场景中,数据会根据一定的策略(如业务逻辑、哈希算法等)被分散到不同的数据库或表中,以提高系统的并发处理能力和扩展性。具体来说,分库分表路由组件需要完成以下任务:数据源的......
  • Java学习第二天——基础语法
    Java基础语法数据类型强类型语言要求变量的使用要严格符合规定,所有变量都必须先定义后才能使用!!!Java的数据类型分类基本类型(primitivetype)1.数值类型整数类型浮点类型字符类型(只占有两个字节)2.boolean类型:占一位,其值为true或者false引用类型(referencetype)类、接......
  • WPF线程模型
    1.渲染系统概述WPF采用保留模式渲染系统(RetainedModeRenderingSystem),该系统可分为UI线程和复合线程两个主要部分,两者协作完成WPF应用程序的渲染工作。1.1立即模式GUI和保持模式GUI图形API可分为保留模式API和即时模式API。Direct2D是一种即时模式API。WPF......
  • Java基础知识篇02——Java基本语法
    一、数据类型定义:就是用了保存数据的一个类型,一种数据类型,只能保存该类型数据值作用:只有了解数据类型,才能选择合适的类型存放数据,才能更好的利用计算机硬件资源(内存和硬盘等)。不同的数据类型存放数据大小是不同的。数据类型的使用方式就是用来声明一个变量,装数据的。......
  • 多线程面试-基础篇(多线程系列连更,后续可查看合集)
    问:线程和进程的区别?答:根本区别:进程是系统划分资源的最小单位,线程是cpu执行和调度的最基本单位。进程包含线程,进程内可包含多个线程。问:为什么要使用多线程?答:提高cpu利用率,并且线程切换和调度成本远小于进程。问:什么是线程死锁?如何避免线程死锁?线程死锁是指,在多线程情况下,A......
  • 程序人生——Java枚举和注解使用建议
    目录引出枚举和注解建议83:推荐使用枚举定义常量建议84:使用构造函数协助描述枚举项建议85:小心switch带来的空值异常建议86:在switch的default代码块中增加AssertionError错误建议87:使用valueOf前必须进行校验建议88:用枚举实现工厂方法模式更简洁建议89:枚举项的数量控制......
  • 程序人生——Java泛型和反射的使用建议
    目录引出泛型和反射建议93:Java的泛型是类型擦除的建议94:不能初始化泛型参数和数组建议95:强制声明泛型的实际类型建议96:不同的场景使用不同的泛型通配符建议97:警惕泛型是不能协变和逆变的建议98:建议采用的顺序是List,List,List建议99:严格限定泛型类型采用多重界限建议1......
  • Java访问者模式源码剖析及使用场景
    访问者模式一、介绍二、报表系统开发三、MyBatis中如何使用访问者模式?一、介绍Java中的访问者(Visitor)模式是一种行为型设计模式,它将数据结构与数据操作分离,使得在不修改数据结构的情况下可以增加新的操作。该模式主要包含以下几个角色:抽象访问者(Visitor):定......