Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >线程会很感谢你的,给它一个可以通信的传话筒

线程会很感谢你的,给它一个可以通信的传话筒

原创
作者头像
JavaSouth南哥
发布于 2024-05-30 09:16:19
发布于 2024-05-30 09:16:19
2530
举报
文章被收录于专栏:JavaSouth系列JavaSouth系列

每个Java线程开始运行,按照你写的逻辑一步步的执行着,就像一个可怜的脚本,孤独地活着!只有它们内部的栈空间、程序计数器在陪伴着它们。

大家给可怜的单线程多加几个玩伴吗?创建一个多线程环境,给它们一个传话筒,让它们可以互相通信、互相问好、嬉皮打闹,看来单线程会很感谢你的。

在这里插入图片描述
在这里插入图片描述

本文收录在我开源的《Java学习面试指南》中,一份覆盖Java程序员所需掌握的Java核心知识、面试重点。希望收到大家的 ⭐ Star ⭐支持。GitHub地址:https://github.com/hdgaadd/JavaGetOffer,相信你看了一定不会后悔。

1. 线程通信

1.1 线程的等待/通知机制

面试官:Java线程的等待/通知机制知道吧?

Java线程的等待/通知机制指的是:线程A获得了synchronized同步方法、同步方法块的锁资源后,调用了锁对象的wait()方法,释放锁的同时进入等待状态;而线程B获得锁资源后,再通过锁对象的notify()或notifyAll()方法来通知线程A恢复执行逻辑。

其实Java的所有对象都拥有等待/通知机制的本领,大家可以在JDK源码package java.lang`下找到Java.lang.Object里提供的五个与等待/通知机制相关的方法。

一、等待。

(1)使当前线程等待,直到另一个线程调用此对象的notify()方法或notifyAll()方法。

代码语言:java
AI代码解释
复制
    public final void wait() throws InterruptedException {
        wait(0);

(2)使当前线程等待,直到另一个线程调用此对象的notify()方法或notifyAll()方法,或者指定的毫秒timeout过去。

代码语言:java
AI代码解释
复制
    public final native void wait(long timeout) throws InterruptedException;

(3)使当前线程等待,直到另一个线程调用此对象的notify()方法或notifyAll()方法,或者指定的毫秒timeout过去,另外nanos是额外时间,以纳秒为单位。

代码语言:java
AI代码解释
复制
    public final void wait(long timeout, int nanos) throws InterruptedException {
    }

所以其实wait()、watit(0)、watit(0, 0)执行后都是同样的效果

二、通知。

(1)唤醒在此对象监视器上等待的单个线程。

代码语言:java
AI代码解释
复制
    public final native void notify();

(2)唤醒在此对象监视器上等待的所有线程。

代码语言:java
AI代码解释
复制
    public final native void notifyAll();

大家有没听说过消费者生产者问题呢?消费者生产者之间要无限循环生产和消费物品,解决之道就是两者形成完美的等待、通知机制。而这套机制就可以通过上文的wait、notify方法来实现。

1.2 线程通信方式

面试官:还有没有其他线程通信方式?

(1)利用Condition进行线程通信。

如果大家的程序直接采用的是Lock对象来同步,则没有了上文synchronized锁带来的隐式同步器,也就无法使用wait()、notify()方法。

此时的线程可以使用Condition对象来进行通信。例如下文的示例代码: condition0的await()阻塞当前线程,同时释放、等待获取锁资源;接着等待其他线程调用condition0的signal()来通知其获取锁资源继续执行。

代码语言:java
AI代码解释
复制
@Slf4j
public class UseReentrantLock {

    private static final ReentrantLock lock = new ReentrantLock();

    private static final Condition condition0 = lock.newCondition();

    private static final Condition condition1 = lock.newCondition();

    public static void main(String[] args) {
        new Thread(() -> {
            try {
                lock.lock();

                for (int i = 1; i < 4; i++) {
                    log.info(i + "");

                    condition1.signal();
                    condition0.await();
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }).start();

        new Thread(() -> {
            try {
                lock.lock();

                for (int i = 65; i < 68; i++) {
                    log.info((char) i + "");

                    condition0.signal();
                    condition1.await();
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }).start();
    }
}
代码语言:sh
AI代码解释
复制
# 程序执行结果
2024-05-30 10:30:30[ INFO ]1
2024-05-30 10:30:30[ INFO ]A
2024-05-30 10:30:30[ INFO ]2
2024-05-30 10:30:30[ INFO ]B
2024-05-30 10:30:30[ INFO ]3
2024-05-30 10:30:30[ INFO ]C

(2)Thread采用join方法进行通信。

线程Thread对象还提供了join方法,也是一种通信的方式。当某个程序的执行流调用了某个thread对象的join方法,调用线程将会被阻塞,等到thread对象终止后才通知调用线程继续执行。

代码语言:java
AI代码解释
复制
    public final void join() throws InterruptedException {
        join(0);
    }

(3)volatile共享变量。

volatile的出现,大家是不是有些意外呢?虽然volatile适用的多线程场景不多,但它也是线程通信的一种方式。被volatile修饰的变量如果更新了值,则会通过主内存这条消息总线通知所有使用该变量的线程,让其把主内存同步到工作内存里,则所有线程都会获取共享变量最新值。

1.3 更加灵活的ReentrantLock

面试官:你说的Lock对象说下你的理解?

在线程同步上,JDK的Lock接口提供了多个实现子类,如下所示。下面我按面试官面试频率高的ReentrantLock来讲解。

在这里插入图片描述
在这里插入图片描述

ReentrantLock相比synchronized来说使用锁更加灵活,可以自由进行加锁、释放锁。ReentrantLock类提供了lock()、unlock()来实现以上操作。具体实操代码可以看上一个面试官问题关于Condition的示例代码。

代码语言:java
AI代码解释
复制
// ReentrantLock源码
package java.util.concurrent.locks;
public class ReentrantLock implements Lock, java.io.Serializable {
    
    // 获取锁
    public void lock() {
        sync.lock();
    }
    
    // 尝试释放此锁
    public void unlock() {
        sync.release(1);
    }
}

另外ReentrantLock和synchronized都是可重入锁,即线程获取锁资源后,下一步如果进入相同锁资源的同步代码块,不需要再获取锁。

ReentrantLock也可以实现公平锁,即成功获取锁的顺序与申请锁资源的顺序一致。我们在创建对象时进行初始化设置就可以设置为公平锁。

代码语言:java
AI代码解释
复制
 ReentrantLock lock = new ReentrantLock(true);

2. ThreadLocal作用

面试官:ThreadLocal知道吧?

上文我们讨论的都是在多个线程对共享资源进行通信的业务场景上,例如商城业务秒杀的库存要保证数据安全性。而如果在多个线程对共享资源进行线程隔离的业务场景上,则可以使用ThreadLoccal来解决。

ThreadLocal可以保存当前线程的副本值,提供了set、get方法,通过set方法可以把指定值设置到当前线程副本;而通过get方法可以返回此当前线程副本中的值。

例如要实现一个功能,每个线程打印当前局部变量:局部变量 + 10,我们就可以利用ThreadLocal保存共享变量i,来避免对变量i的共享冲突。

代码语言:java
AI代码解释
复制
public class UseThreadLocal {
    public static void main(String[] args) {
        ExecutorService es = Executors.newFixedThreadPool(20);
        for (int i = 0; i < 3; i++) {
            int number = i;
            es.execute(() -> System.out.println(number + ":" + new intUtil().addTen(number)));
        }
    }

    private static class intUtil {
        public static ThreadLocal<Integer> threadLocal = new ThreadLocal<>(); // 使用threadLocal保存线程保存的当前共享变量num

        public static int addTen(int number) {
            threadLocal.set(number);

            try { // 休息1秒
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return threadLocal.get() + 10;
        }
    }
}
代码语言:sh
AI代码解释
复制
# 程序执行结果
0:10
2:12
1:11

3. 线程生命周期

面试官:那线程生命周期都有什么?

在校招笔试、或面试中,这道面试题还是比较常见的,大家简单记忆下就可以。

  1. 初始状态。创建了线程对象还没有调用start()。
  2. 就绪或运行状态。执行了start()可能运行,也可能进入就绪状态在等待CPU资源。
  3. 阻塞状态 。一直没有获得锁。
  4. 等待状态。等待其他线程的通知唤醒。
  5. 超时状态。
  6. 终止状态。

创作不易,不妨点赞、收藏、关注支持一下,各位的支持就是我创作的最大动力❤️

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
高并发编程学习(2)——线程通信详解
上一篇文章我们提到一个应用可以创建多个线程去执行不同的任务,如果这些任务之间有着某种关系,那么线程之间必须能够通信来协调完成工作。
乔戈里
2019/12/02
5140
高并发编程学习(2)——线程通信详解
手撕面试题:多个线程顺序执行问题
大家在换工作面试中,除了一些常规算法题,还会遇到各种需要手写的题目,所以打算总结出来,给大家个参考。
海星
2020/10/30
2.9K0
手撕面试题:多个线程顺序执行问题
线程间通信的几种实现方式
基于 volatile 关键字来实现线程间相互通信是使用共享内存的思想,大致意思就是多个线程同时监听一个变量,当这个变量发生变化的时候 ,线程能够感知并执行相应的业务。这也是最简单的一种实现方式
全栈程序员站长
2022/09/06
5910
线程间通信的几种实现方式
高并发编程学习(2)——线程通信详解
上一篇文章我们提到一个应用可以创建多个线程去执行不同的任务,如果这些任务之间有着某种关系,那么线程之间必须能够通信来协调完成工作。
我没有三颗心脏
2019/11/29
4490
java面试线程必备知识点,怼死面试官,从我做起
内存屏障:限制命令操作顺序,有LoadLoad、LoadStore、LoadStore、StroreStreo四种屏障
用户1257393
2018/07/30
3620
多线程知识:三个线程如何交替打印ABC循环100次
本文博主给大家讲解一道网上非常经典的多线程面试题目。关于三个线程如何交替打印ABC循环100次的问题。
wayn
2023/07/07
1K0
多线程知识:三个线程如何交替打印ABC循环100次
juc05--线程通信
核心:利用共享对象实现通信,这里的通信不是指传值,而是发送信号。 目的:就是让线程间具有互相发送信号通信的能力。 而且,线程通信可以实现,一个线程可以等待来自其他线程的信号。 举个例子,一个线程B可能正在等待来自线程A的信号,这个信号告诉线程B数据已经处理好了。
潇洒
2023/10/20
1930
Java多线程04——线程通信
线程是操作系统中的独立个体,但这些个体如果不经过特殊处理就不能成为一个整体,线程间的通信就成为整体的必用方式之一。例如之前处理的线程同步,就是一种线程间通信的方式。
头发还在
2023/10/16
1760
Java多线程04——线程通信
Java多线程并发编程一览笔录
知识体系图: 1、线程是什么? 线程是进程中独立运行的子任务。 2、创建线程的方式 方式一:将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法 方式二:声明实现 Runn
斯武丶风晴
2018/03/01
8820
Java多线程并发编程一览笔录
8.Condition 控制线程通信
前一篇我们讲述了 同步锁 Lock,那么下面肯定就要讲解一下 同步锁 Lock 如何控制线程之间的通讯。
Devops海洋的渔夫
2022/03/23
3770
8.Condition 控制线程通信
面试官:线程是如何通讯的?
线程通信是指多个线程之间通过某种机制进行协调和交互,例如,线程等待和通知机制就是线程通讯的主要手段之一。
磊哥
2023/08/03
1770
面试官:线程是如何通讯的?
浅谈 JAVA 并发编程的有锁和无锁机制
本篇文章是笔者在梳理并发编程相关技术时的笔记整理,原先的目的是期望研究关于 JVM 中对于无锁机制的基本原理,在此过程中因为各技术点之间关联牵扯较多,因此还是先将 JVM 锁机制抛出作为一个引子,进而再通过对比分析的方式来解释无锁机制的实现思路。本文主要包括 3 个部分:
磊叔的技术博客
2025/06/09
1.5K0
浅谈 JAVA 并发编程的有锁和无锁机制
并发多线程学习(六)Java线程间的通信
合理的使用Java多线程可以更好地利用服务器资源。一般来讲,线程内部有自己私有的线程上下文,互不干扰。但是当我们需要多个线程之间相互协作的时候,就需要我们掌握Java线程的通信方式。本文将介绍Java线程之间的几种通信原理。
用户10168815
2023/02/28
3450
【JavaSE专栏80】多线程通信,多个线程之间如何实现信息传递和同步?
本文讲解了 Java 中多线程通信的语法和应用场景,并给出了样例代码。多线程通信是指多个线程之间通过共享的对象或变量进行信息传递和同步的过程,多线程通信的目的是实现线程之间的协调工作,使得线程能够有效地协作完成任务。
Designer 小郑
2023/08/19
1.7K0
【JavaSE专栏80】多线程通信,多个线程之间如何实现信息传递和同步?
线程间的通信
何时使用 在多线程环境下,有时候一个线程的执行,依赖于另外一个线程的某种状态的改变,这个时候,我 们就可以使用wait与notify或者notifyAll wait跟sleep的区别 wait会释放持有的锁,而sleep不会,sleep只是让线程在指定的时间内,不去抢占cpu的资 源 注意点 wait notify必须放在同步代码块中, 且必须拥有当前对象的锁,即不能取得A对象的锁,而调用B对象 的wait 哪个对象wait,就得调哪个对象的notify notify跟notifyAll的区别 nofity随机唤醒一个等待的线程 notifyAll唤醒所有在该对象上等待的线程
Dream城堡
2019/05/20
4770
【JUC基础】06. 生产者和消费者问题
学习JUC,就不得不提生产者消费者。生产者消费者模型是一种经典的多线程模型,用于解决生产者和消费者之间的数据交换问题。在生产者消费者模型中,生产者生产数据放入共享的缓冲区中,消费者从缓冲区中取出数据进行消费。在这个过程中,生产者和消费者之间需要保持同步,以避免数据出现错误或重复。今天我们就来说说生产者消费者模型,以及JUC中如何解决该模型的同步问题。
有一只柴犬
2024/01/25
2230
【JUC基础】06. 生产者和消费者问题
你真的知道线程间是如何通信的么?
线程启动后,它会在自己独有的栈空间里面运行,但是实际上,两个线程之间是会相互通信的,因为只有这样才能使线程间更加灵活,使资源使用的更加充分。
AI码师
2022/09/19
3790
你真的知道线程间是如何通信的么?
Java--线程同步&线程通信
上一篇--五态模型&控制线程 线程同步: 同步监视器(synchronized):  Java多线程引出了临界区问题。当两个进程并发修改同一资源时就有可能造成异常。Java引入同步监视器来解决这个问题。使用同步监视器有两种方法:同步代码块和同步方法。 同步代码块: synchronized(obj){ //此处代码是同步代码块 } 上述代码的obj就是同步监视器,同一时刻只能有一个线程获得同步监视器的锁定,当同步代码块执行完毕后,该线程会自动释放该同步监视器的锁定。 通常使用可能被并发访问的共享资源
SuperHeroes
2018/05/22
1.1K0
Android并发编程 多线程与锁
该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,如果能给各位看官带来一丝启发或者帮助,那真是极好的。
LoveWFan
2018/11/30
9290
Java多线程学习(六)Lock锁的使用
我自己总结的Java学习的系统知识点以及面试问题,目前已经开源,会一直完善下去,欢迎建议和指导欢迎Star: https://github.com/Snailclimb/Java-Guide
用户2164320
2018/06/23
11.5K0
Java多线程学习(六)Lock锁的使用
相关推荐
高并发编程学习(2)——线程通信详解
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档