第二章:《情窦初开:多线程的甜蜜相遇》

2024-05-29 07:44

本文主要是介绍第二章:《情窦初开:多线程的甜蜜相遇》,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

多线程的爱之初体验

多线程,就是在同一程序中同时执行多个任务的能力,就像是苦逼程序猿凯叔从单身到恋爱生活的转变。在单线程的世界里,程序就像一个孤独的程序猿,只能按部就班地逐一完成任务,好比单身时的生活,一个人做饭、洗碗、工作,一切都得自己来,顺序进行,效率受限。

而一旦引入多线程,情况就大不相同了。这就像程序猿突然找到了另一半,生活开始有了搭档,两个人可以同时处理不同的事情:一个做饭,另一个洗碗;一个忙着敲代码,另一个则处理文档,彼此协作,任务完成得又快又好。在程序中,多线程允许不同的线程并行处理不同的任务或同一任务的不同部分,大大提升了效率和响应速度,就好比恋爱中的两人互相支持,共同进步,生活变得更加高效和谐。

总结来说,多线程的引入如同单身到恋爱的转变,从独自承担所有责任到有了伴侣的协助与分担,不仅让程序执行变得更加灵活高效,也象征着生活中的合作与成长。

PS:但是这只是比喻,真正的凯叔的生活是一个人做饭、洗碗、工作,还得带吞金兽,还得上交“保护费”狗日的多线程。啊呸!

在这里插入图片描述

多线程的引入对比单线程来说给程序猿们更多无限遐想的空间,现在的系统动不动就要求百万级甚至千万级的并发量,而多线程并发编程正式开发高并发系统的基础,利用好多线程机制可以大大提高系统的并发能力以及性能。多线程正象是一个完美的时间管理大师。

开启多线程:

敲黑板,划重点,面试必备有考点

Java多线程的实现方式主要有以下四种,每种方式有其特定的应用场景和优缺点:

  1. 继承Thread类
    • 实现方式:创建一个新的类继承Thread类,并重写其run()方法,在run()方法中定义需要并行执行的代码逻辑。然后创建该类的实例并调用start()方法启动新线程。

    • 区别与特点:这种方式简单直接,但因为Java不支持多重继承,如果你的类已经继承了其他类,则无法再继承Thread类来创建线程。

     public class ThreadDemo1 extends Thread {@Overridepublic void run() {System.out.println("继承Thread实现多线程,名	称:"+Thread.currentThread().getName());}}public static void main(String[] args) {ThreadDemo1 threadDemo1 = new ThreadDemo1();threadDemo1.setName("demo1");threadDemo1.start();System.out.println("主线程名称:"+Thread.currentThread().getName());}
  1. 实现Runnable接口
  • 实现方式:定义一个类实现Runnable接口,并实现其run()方法。然后将此Runnable实例作为参数传递给Thread类的构造函数,创建Thread对象,最后通过Thread对象的start()方法启动线程。
  • 区别与特点:由于Java支持接口的多重实现,这种方式更加灵活,可以在同一个类中实现多个接口,更适合复杂的类继承结构。
    public class ThreadDemo2 implements Runnable {@Overridepublic void run() {System.out.println("通过Runnable实现多线程,名称:"+Thread.currentThread().getName());}}public static void main(String[] args) {ThreadDemo2 threadDemo2 = new ThreadDemo2();Thread thread = new Thread(threadDemo2);thread.setName("demo2");thread.start();System.out.println("主线程名称:"+Thread.currentThread().getName());}//JDK8之后采⽤lambda表达式public static void main(String[] args) {Thread thread = new Thread(()->{System.out.println("通过Runnable实现多线程,名称:"+Thread.currentThread().getName());});thread.setName("demo2");thread.start();System.out.println("主线程名称:"+Thread.currentThread().getName());}
  1. 实现Callable接口 + FutureTask包装器
    • 实现方式:创建一个类实现Callable接口,该接口有一个call()方法可以返回结果并抛出异常。然后使用FutureTask包装器将Callable对象转换为Runnable,再将FutureTask交给Thread去执行,或者提交给ExecutorService管理。

    • 区别与特点:与前两种方式相比,Callable可以返回结果并声明抛出异常,提供了更强的灵活性和错误处理能力,适用于需要获取线程执行结果的场景。

    //缺点:jdk5以后才⽀持,需要重写call⽅法,结合多个类⽐如FutureTask和Thread类public class MyTask implements Callable<Object> {@Overridepublic Object call() throws Exception {System.out.println("通过Callable实现多线程,名 称:"+Thread.currentThread().getName());return "这是返回值";}}public static void main(String[] args) {FutureTask<Object> futureTask = new FutureTask<>(()->{System.out.println("通过Callable实现多线程,名称:"+Thread.currentThread().getName());return "这是返回值";});//FutureTask继承了Runnable,可以放在Thread中启动执⾏Thread thread = new Thread(futureTask);thread.setName("demo3");thread.start();System.out.println("主线程名 称:"+Thread.currentThread().getName());try {System.out.println(futureTask.get());} catch (InterruptedException e) {//阻塞等待中被中断,则抛出e.printStackTrace();} catch (ExecutionException e) {//执⾏过程发送异常被抛出e.printStackTrace();}}

​ 4.使用Executor框架(如ThreadPoolExecutor)

  • 实现方式:通过Executors工厂类创建线程池(如固定大小线程池、缓存线程池、定时线程池等),然后提交Runnable或Callable任务给线程池执行。

  • 实现方式:这种方式提供了线程管理和调度的高级功能,如线程复用、任务调度、线程池管理等,能有效控制线程数量,提高系统资源利用率,降低系统开销,是生产环境中推荐使用的多线程编程模式。

//优点:安全⾼性能,复⽤线程
//缺点: jdk5后才⽀持,需要结合Runnable进⾏使⽤   
public class ThreadDemo4 implements Runnable {@Overridepublic void run() {System.out.println("通过线程池+runnable实现多线程,名称:"+Thread.currentThread().getName());}}public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(3);for(int i=0;i<10;i++){executorService.execute(new ThreadDemo4());}System.out.println("主线程名称:"+Thread.currentThread().getName());//关闭线程池executorService.shutdown();}

总结区别

  • 继承Thread类实现Runnable接口是最基本的实现方式,后者因为支持接口多重继承而更灵活。
  • 实现Callable接口相比前两者,增加了返回结果和异常处理的能力,适合需要线程执行结果的场景。
  • Executor框架引入了线程池的概念,提供了更高层次的抽象,简化了线程管理,提高了程序性能和可维护性,是现代多线程编程的首选方案。

同步与异步的恋爱哲学

在Java多线程编程中,同步和异步是处理线程间交互和资源共享时两种基本的方法,它们在设计理念和实现机制上有着本质的区别,但又常常在实际应用中相辅相成,共同保证了程序的正确性和高效性。

就好比同步就是我给你发消息,你秒回,这就是同步;异步就是我给你发消息,你这时候正在跟别的哥哥聊的正嗨,晚上了看到消息,回了一个“哦,我要去洗澡了,下次聊”,而你还贴心想“每次女神为了我都要专门去洗澡,这就是爱情”。舔狗不得House.

同步(Synchronization)

在Java中,同步主要用于保护共享资源不被多个线程同时访问而造成数据不一致或逻辑错误。主要有以下几种同步机制:

  1. synchronized关键字:这是Java中最常用的同步机制,它可以用来修饰方法或代码块。当一个线程访问某个对象的synchronized方法或代码块时,其他线程必须等待,直到当前线程完成操作。synchronized基于监视器锁(monitor lock)实现,确保了同一时刻只有一个线程可以执行受保护的代码。
  2. Lock接口及其实现类(如ReentrantLock):提供比synchronized更细粒度的锁控制,例如tryLock()方法尝试获取锁,可以设定超时时间,以及支持公平锁和非公平锁的选择。
  3. 原子类(AtomicInteger, AtomicBoolean等):通过CAS(Compare and Swap)操作实现无锁同步,适用于轻量级的同步需求。
异步(Asynchronous)

异步编程模型关注的是非阻塞操作,即当一个操作可能需要较长时间完成时(如I/O操作、网络通信等),程序不等待其完成,而是继续执行其他任务,待操作完成后通过回调、事件、Future/Promise等方式通知主线程或处理结果。

  1. 回调:异步操作完成后执行的函数,通常作为参数传递给异步方法。例如,在网络请求中,可以指定一个回调函数来处理响应数据。
  2. Future和CompletableFuture:Java提供的用于表示异步计算结果的类。Future可以查看计算是否完成,获取结果(可能阻塞),而CompletableFuture在此基础上增加了链式调用、组合异步操作的能力,支持更复杂的异步编程模式。
  3. Reactive Programming(反应式编程):通过Observable、Flowable等模型,以声明式的方式处理异步数据流,提供了强大的背压(backpressure)机制来应对生产者消费者速度不匹配问题。
同步与异步的区别和联系

区别:同步操作会导致线程等待,保证了操作的顺序执行和数据的一致性,但可能引起性能瓶颈;异步操作则不阻塞线程,提高了程序的并发能力,但编程复杂度相对较高,需要处理并发控制和结果回调。

联系:在实际应用中,同步和异步经常结合起来使用。例如,可以在异步处理完成后,通过同步代码更新共享状态,确保数据一致性。两者都是多线程编程中处理并发问题的有效工具,选择使用哪种方式取决于具体场景的需求和权衡。

线程的几种状态转换关系

在这里插入图片描述
未完待续

这篇关于第二章:《情窦初开:多线程的甜蜜相遇》的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

多线程解析报表

假如有这样一个需求,当我们需要解析一个Excel里多个sheet的数据时,可以考虑使用多线程,每个线程解析一个sheet里的数据,等到所有的sheet都解析完之后,程序需要提示解析完成。 Way1 join import java.time.LocalTime;public class Main {public static void main(String[] args) thro

Java 多线程概述

多线程技术概述   1.线程与进程 进程:内存中运行的应用程序,每个进程都拥有一个独立的内存空间。线程:是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换、并发执行,一个进程最少有一个线程,线程实际数是在进程基础之上的进一步划分,一个进程启动之后,进程之中的若干执行路径又可以划分成若干个线程 2.线程的调度 分时调度:所有线程轮流使用CPU的使用权,平均分配时间抢占式调度

Java 多线程的基本方式

Java 多线程的基本方式 基础实现两种方式: 通过实现Callable 接口方式(可得到返回值):

JAVA- 多线程

一,多线程的概念 1.并行与并发 并行:多个任务在同一时刻在cpu 上同时执行并发:多个任务在同一时刻在cpu 上交替执行 2.进程与线程 进程:就是操作系统中正在运行的一个应用程序。所以进程也就是“正在进行的程序”。(Windows系统中,我们可以在任务管理器中看 到进程) 线程:是程序运行的基本执行单元。当操作系统执行一个程序时, 会在系统中建立一个进程,该进程必须至少建立一个线

多线程篇(阻塞队列- LinkedBlockingDeque)(持续更新迭代)

目录 一、LinkedBlockingDeque是什么 二、核心属性详解 三、核心方法详解 addFirst(E e) offerFirst(E e) putFirst(E e) removeFirst() pollFirst() takeFirst() 其他 四、总结 一、LinkedBlockingDeque是什么 首先queue是一种数据结构,一个集合中

多线程篇(阻塞队列- LinkedBlockingQueue)(持续更新迭代)

目录 一、基本概要 1. 构造函数 2. 内部成员 二、非阻塞式添加元素:add、offer方法原理 offer的实现 enqueue入队操作 signalNotEmpty唤醒 删除线程(如消费者线程) 为什么要判断if (c == 0)时才去唤醒消费线程呢? 三、阻塞式添加元素:put 方法原理 图解:put线程的阻塞过程 四、非阻塞式移除:poll方法原理 dequ

spring笔记 多线程的支持

spring的工作机制 136  属性编辑器 140 spring事件的体系结构 168 Bean间的关系 109 继承 依赖 引用     Bean的继承          1 为了简化初始化的属性注入;          2 子Bean和父Bean相同的属性值,使用子Bean的     Bean的依赖 Srping控制相互依赖的Bean之间,属性注入的顺序,防止出错  depend-on

【编程底层思考】详解Java的JUC多线程并发编程底层组件AQS的作用及原理

Java中的AbstractQueuedSynchronizer(简称AQS)是位于java.util.concurrent.locks包中的一个核心组件,用于构建锁和其他同步器。AQS为实现依赖于FIFO(先进先出)等待队列的阻塞锁和相关同步器提供了一套高效、可扩展的框架。 一、AQS的作用 统一同步状态管理:AQS提供了一个int类型的成员变量state,用于表示同步状态。子类可以根据自己

多线程的系列文章

Java多线程学习(一)Java多线程入门 Java多线程学习(二)synchronized关键字(1)   Java多线程学习(二)synchronized关键字(2) Java多线程学习(三)volatile关键字 Java多线程学习(四)等待/通知(wait/notify)机制 Java多线程学习(五)线程间通信知识点补充 Java多线程学习(六)Lock锁的使用 Java多

多线程 线程池的创建

一简介 线程的使用在java中占有极其重要的地位,在jdk1.4极其之前的jdk版本中,关于线程池的使用是极其简陋的。在jdk1.5之后这一情况有了很大的改观。Jdk1.5之后加入了java.util.concurrent包,这个包中主要介绍java中线程以及线程池的使用。为我们在开发中处理线程的问题提供了非常大的帮助。 二:线程池 线程池的作用: 线程池作用就是限制系统中执行线程的数量。