从新手到大师:Java并发编程你必须知道的那些事!

2024-09-06 19:36

本文主要是介绍从新手到大师:Java并发编程你必须知道的那些事!,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述

文章目录

    • 1 进程和线程的区别?
    • 2 如何创建一个线程实例并且运行它?
    • 3 Runnable 和 Callable 接口有什么区别?它们是如何使用的?
    • 4 方法定义中 synchronized 关键字的含义是什么?静态方法?在一个块之前 ?

1 进程和线程的区别?

进程是独立的执行单元,拥有自己的资源和内存,而线程是在进程内的执行单元,共享进程的资源。线程可以高效地执行任务,但需要注意共享资源时的同步问题。进程则提供了更高的隔离度,适用于需要较强隔离的场景。

进程可以看作是计算机上运行的程序的一个实例。每个进程都有自己的内存空间和资源,像是一座独立的工厂,每个工厂都拥有自己的机器、员工和原材料,相互之间没有直接的干扰。

进程之间的通信和数据交换比较复杂,通常需要通过操作系统提供的机制,比如管道、文件或网络进行。

进程是操作系统中资源分配的基本单位,每个进程都有一个唯一的进程标识符(PID)。

线程则是进程中的一个执行单元。可以把线程看作是工厂中的一个员工,它们共享工厂的资源,如机器和原材料,但每个员工有自己的工作任务。

线程之间可以很方便地共享数据和资源,因为它们都在同一个进程内,线程的创建和销毁开销比进程小得多,但因为共享资源,线程之间的协调和同步变得更加重要。

实际例子,比如浏览器。每个浏览器窗口或标签页通常就是一个进程,这样即使一个标签页崩溃,其他标签页也不会受影响。每个标签页中的操作,比如加载网页、执行JavaScript脚本等,都是由不同的线程来处理的,这些线程共享同一个标签页的内存空间,从而可以更高效地进行任务。

创建一个新的进程可能需要比较大的开销,因为操作系统需要为这个进程分配独立的内存空间和资源。

使用 ProcessBuilder 在Java中创建新进程

ProcessBuilder processBuilder = new ProcessBuilder("text.exe");
// 启动一个新的进程,独立于当前进程运行
Process process = processBuilder.start();

相比之下,创建线程的开销要小得多,线程可以在同一进程内共享资源。

创建线程Demo

public class MyThread extends Thread {public void run() {System.out.println("Thread is running");}
}public class Main {public static void main(String[] args) {MyThread thread = new MyThread();// 启动一个新的线程,这个线程与主线程共享相同的进程资源thread.start(); }
}

2 如何创建一个线程实例并且运行它?

创建和运行一个线程实例是进行多任务处理的重要步骤,线程允许程序同时执行多个任务,提高了应用的效率和响应能力。

通过定义一个类来创建一个线程实例,这个类通常需要继承自一个线程基类,或者实现一个线程接口。

线程的创建过程涉及到两个主要步骤:定义线程的工作内容启动线程

定义线程的工作内容的方式有多种。一种常见的方法是创建一个新的类,并继承自线程类。这个新类需要重写一个特定的方法,这个方法中包含了线程在运行时要执行的任务。

定义一个线程类Demo:

public class MyThread extends Thread {@Overridepublic void run() {// 线程要执行的任务System.out.println("运行ing");}
}

创建线程实例后,接下来需要启动线程,通过调用线程实例的 start 方法完成的。调用 start 方法会让线程进入就绪状态,然后操作系统会调度线程的执行。调用 start 方法不会直接运行线程,而是将线程放入一个待执行的队列中,操作系统会选择合适的时机来执行它。

创建并启动线程Demo

public class Main {public static void main(String[] args) {// 创建线程实例MyThread thread = new MyThread();// 启动线程,触发 MyThread 的 run 方法执行thread.start();}
}

在另一种方法中,也可以通过实现一个线程接口来创建线程。假设有一个接口 Runnable,其中包含一个 run 方法。在实现这个接口的类中,run 方法同样需要包含线程的任务代码。通过将实现了 Runnable 接口的对象传递给一个线程实例,可以实现线程的创建和运行。

实现 Runnable 接口Demo:

public class MyRunnable implements Runnable {@Overridepublic void run() {// 线程要执行的任务System.out.println("运行ing");}
}public class Main {public static void main(String[] args) {// 创建 Runnable 实例MyRunnable myRunnable = new MyRunnable();// 创建线程实例,将 Runnable 实例传递给线程Thread thread = new Thread(myRunnable);// 启动线程thread.start();}
}

3 Runnable 和 Callable 接口有什么区别?它们是如何使用的?

RunnableCallable 用于并发编程的两个接口,它们允许在多线程环境中执行任务,但有一些重要的区别。

Runnable 接口定义了一个方法 run(),这个方法中的代码是线程执行的主体。

Runnable 适合用于不需要返回结果的任务,比如更新用户界面或处理日志,它无法直接返回结果,且不能抛出受检查异常Runnable接口的Demo在上方

Callable 接口不仅定义了任务的代码,还允许任务返回一个结果

Callablecall() 方法会返回一个值,这个值可以用来表示任务的结果。与 Runnable 不同,Callable 可以抛出受检查异常,这让它能处理更复杂的错误情况。为了获取 Callable 执行的结果,通常需要配合 Future 对象使用。

实现 Callable 接口Demo:

import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;public class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {// 返回一个结果return 42; }
}public class Main {public static void main(String[] args) throws Exception {ExecutorService executor = Executors.newSingleThreadExecutor();Future<Integer> future = executor.submit(new MyCallable());// 获取 Callable 任务的结果Integer result = future.get(); System.out.println("任务结果: " + result);executor.shutdown();}
}

RunnableCallable 的选择取决于任务是否需要返回值和是否需要处理受检查异常。Runnable 适合简单的任务,Callable 更适合需要返回结果或处理异常的复杂任务。

4 方法定义中 synchronized 关键字的含义是什么?静态方法?在一个块之前 ?

synchronized 关键字用于实现线程同步,解决多线程环境中资源竞争的问题。通过在方法或代码块上使用 synchronized,可以确保同一时间只有一个线程能够执行这段代码,从而避免数据不一致或其他并发问题。

synchronized 关键字用于方法时,它会锁定整个对象

展示了一个实例方法如何使用 synchronized

public class MyClass {public synchronized void synchronizedMethod() {// 每次只有一个线程可以进入这个方法,其他线程必须等待,直到第一个线程执行完成并释放锁}
}

synchronized 用于静态方法,它会锁定整个类的对象

意味着所有该类的实例共享同一个锁,静态方法使用 synchronized

public class MyClass {public static synchronized void synchronizedStaticMethod() {// 确保了在同一时刻,只有一个线程能够执行这个类的静态同步方法}
}

synchronized 用于代码块,而不是整个方法,这样做的好处是可以只锁定代码块中真正需要同步的部分

代码块使用synchronized

public class MyClass {// lock 对象用于控制代码块的访问private final Object lock = new Object();public void someMethod() {// 只有持有 lock 锁的线程才能执行代码块中的代码synchronized (lock) {// 使用代码块同步时,可以控制锁定的范围,从而提高程序的效率和性能}}
}

苦难是人生的老师,通过苦难,走向欢乐

这篇关于从新手到大师:Java并发编程你必须知道的那些事!的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1142929

相关文章

Spring Boot @RestControllerAdvice全局异常处理最佳实践

《SpringBoot@RestControllerAdvice全局异常处理最佳实践》本文详解SpringBoot中通过@RestControllerAdvice实现全局异常处理,强调代码复用、统... 目录前言一、为什么要使用全局异常处理?二、核心注解解析1. @RestControllerAdvice2

Python中你不知道的gzip高级用法分享

《Python中你不知道的gzip高级用法分享》在当今大数据时代,数据存储和传输成本已成为每个开发者必须考虑的问题,Python内置的gzip模块提供了一种简单高效的解决方案,下面小编就来和大家详细讲... 目录前言:为什么数据压缩如此重要1. gzip 模块基础介绍2. 基本压缩与解压缩操作2.1 压缩文

Spring IoC 容器的使用详解(最新整理)

《SpringIoC容器的使用详解(最新整理)》文章介绍了Spring框架中的应用分层思想与IoC容器原理,通过分层解耦业务逻辑、数据访问等模块,IoC容器利用@Component注解管理Bean... 目录1. 应用分层2. IoC 的介绍3. IoC 容器的使用3.1. bean 的存储3.2. 方法注

Spring事务传播机制最佳实践

《Spring事务传播机制最佳实践》Spring的事务传播机制为我们提供了优雅的解决方案,本文将带您深入理解这一机制,掌握不同场景下的最佳实践,感兴趣的朋友一起看看吧... 目录1. 什么是事务传播行为2. Spring支持的七种事务传播行为2.1 REQUIRED(默认)2.2 SUPPORTS2

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

Java进程异常故障定位及排查过程

《Java进程异常故障定位及排查过程》:本文主要介绍Java进程异常故障定位及排查过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、故障发现与初步判断1. 监控系统告警2. 日志初步分析二、核心排查工具与步骤1. 进程状态检查2. CPU 飙升问题3. 内存

java中新生代和老生代的关系说明

《java中新生代和老生代的关系说明》:本文主要介绍java中新生代和老生代的关系说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、内存区域划分新生代老年代二、对象生命周期与晋升流程三、新生代与老年代的协作机制1. 跨代引用处理2. 动态年龄判定3. 空间分

Java设计模式---迭代器模式(Iterator)解读

《Java设计模式---迭代器模式(Iterator)解读》:本文主要介绍Java设计模式---迭代器模式(Iterator),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录1、迭代器(Iterator)1.1、结构1.2、常用方法1.3、本质1、解耦集合与遍历逻辑2、统一

Java内存分配与JVM参数详解(推荐)

《Java内存分配与JVM参数详解(推荐)》本文详解JVM内存结构与参数调整,涵盖堆分代、元空间、GC选择及优化策略,帮助开发者提升性能、避免内存泄漏,本文给大家介绍Java内存分配与JVM参数详解,... 目录引言JVM内存结构JVM参数概述堆内存分配年轻代与老年代调整堆内存大小调整年轻代与老年代比例元空

深度解析Java DTO(最新推荐)

《深度解析JavaDTO(最新推荐)》DTO(DataTransferObject)是一种用于在不同层(如Controller层、Service层)之间传输数据的对象设计模式,其核心目的是封装数据,... 目录一、什么是DTO?DTO的核心特点:二、为什么需要DTO?(对比Entity)三、实际应用场景解析