当前位置: 首页 > news >正文

Java面试题五


一、谈谈你对Java内存模型(JVM Memory Model)的理解。


Java内存模型(Java Memory Model,简称JMM)是Java虚拟机(JVM)规范中定义的一种关于内存访问、共享变量在多线程之间的可见性、以及原子性、顺序性的规则。以下是对Java内存模型的详细理解:

一、内存模型概述

Java内存模型描述的是一组规则或规范,这组规范定义了程序中各个变量(包括实例字段、静态字段和构成数组对象的元素)的访问方式。由于JVM运行程序的实体是线程,每个线程在创建时JVM都会为其创建一个工作内存(也称为本地内存或线程栈),用于存储线程私有的数据。而Java内存模型中规定所有变量都存储在主内存中,主内存是共享内存区域,所有线程都可以访问。但线程对变量的操作(读取、赋值等)必须在工作内存中进行,不能直接操作主内存中的变量。

二、主内存与工作内存

  • 主内存:所有线程共享的内存区域,包含了对象的字段、方法和运行时常量池等数据。这是Java堆的一部分,用于存储Java实例对象。
  • 工作内存:每个线程拥有自己的工作内存,工作内存中保存了主内存中变量的副本,线程对变量的所有操作(读取、写入)都在工作内存中进行。工作内存是线程私有的数据区域,因此不同的线程间无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成。

三、三大特性

Java内存模型围绕三大特性展开,即原子性、可见性和有序性。

  • 原子性:一个或多个操作,要么全部执行,要么全部不执行(执行的过程中是不会被任何因素打断的)。Java内存模型通过lock和unlock操作来保证原子性,同时提供了synchronized关键字和Lock接口等机制来实现。
  • 可见性:一个线程对共享变量的修改,能够被其他线程看到。Java内存模型通过volatile关键字和synchronized来保证可见性。当一个线程修改了volatile变量的值,新值对于其他线程来说是立即可见的。同时,synchronized也可以确保线程在进入同步块或同步方法时,能够看到最新的变量值。
  • 有序性:程序的执行顺序按照代码的先后顺序执行。然而,由于编译器的优化和指令集的重排序,Java程序在并发执行时可能会出现乱序执行的情况。Java内存模型通过Happens-Before规则来定义操作之间的偏序关系,从而允许编译器和处理器对指令进行重排序,但同时又保证程序最终执行的结果与按照Happens-Before关系规定的顺序执行的结果一致。

四、Happens-Before规则

Happens-Before是Java内存模型中最核心的概念之一,它定义了一组偏序关系,用于判断两个操作之间的内存可见性和有序性。主要包括以下规则:

  • 程序次序规则:一个线程中的每个操作,Happens-Before于该线程中的任意后续操作。
  • 监视器锁规则:对一个锁的解锁,Happens-Before于随后对这个锁的加锁。
  • volatile变量规则:对一个volatile变量的写,Happens-Before于任意后续对这个volatile变量的读。
  • 传递性:如果A Happens-Before B,且B Happens-Before C,那么A Happens-Before C。
  • 线程启动规则:Thread对象的start()方法调用Happens-Before于该线程的每一个动作。
  • 线程终止规则:线程的所有操作都Happens-Before于其他线程检测到这个线程已经终止、或者从该线程的join()方法调用返回、或者从该线程的Thread.isAlive()方法的返回值为false。
  • 线程中断规则:对线程interrupt()方法的调用Happens-Before于被中断线程的代码检测到中断事件的发生。
  • 最终结束规则:对象的构造函数执行、结束Happens-Before于它的finalize()方法的开始。

五、总结

Java内存模型是一种抽象的规范,它定义了线程和主内存之间的抽象关系,以及共享变量的访问规则。通过保证原子性、可见性和有序性,Java内存模型为程序员提供了一致的内存访问语义,使得多线程程序的行为可预测、可理解。同时,Java内存模型还提供了一系列的同步机制(如volatile关键字、synchronized关键字、Lock接口等),以帮助开发者编写正确和高效的多线程代码。

综上所述,Java内存模型是Java并发编程中的重要概念,理解并掌握它对于编写高效、正确的多线程程序至关重要。


二、Java中如何创建一个线程?有哪些实现方式?


在Java中,线程的创建有多种实现方式,以下是四种主要的方法:

一、继承Thread类

  1. 步骤

    • 定义一个Thread类的子类,并重写其run()方法。run()方法包含线程要执行的业务逻辑。
    • 创建Thread子类的实例。
    • 调用该实例的start()方法来启动线程。
  2. 示例代码

public class MyThread extends Thread {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + " from MyThread");}
}public class MyThreadTest {public static void main(String[] args) {MyThread thread = new MyThread();thread.start();}
}

二、实现Runnable接口

  1. 步骤

    • 定义一个Runnable接口的实现类,并重写其run()方法。
    • 创建Runnable实现类的实例。
    • 使用该实例作为target创建Thread对象。
    • 调用Thread对象的start()方法来启动线程。
  2. 示例代码

public class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + " from MyRunnable");}
}public class MyRunnableTest {public static void main(String[] args) {MyRunnable myRunnable = new MyRunnable();Thread thread = new Thread(myRunnable);thread.start();}
}

三、使用Callable和Future

  1. 步骤

    • 创建一个实现Callable接口的类,并实现其call()方法。call()方法可以有返回值,并且可以声明抛出异常。
    • 使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值。
    • 使用FutureTask对象作为Thread对象的target创建并启动线程。
    • 调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。
  2. 示例代码

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;public class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {System.out.println(Thread.currentThread().getName() + " from MyCallable");return 99;}
}public class MyCallableTest {public static void main(String[] args) {FutureTask<Integer> futureTask = new FutureTask<>(new MyCallable());Thread thread = new Thread(futureTask);thread.start();try {Thread.sleep(1000); // 等待线程执行完成System.out.println("返回的结果是: " + futureTask.get());} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}}
}

四、使用线程池

  1. 步骤

    • 使用Executors工厂类创建线程池,返回的线程池实现了ExecutorService接口。
    • 提交Runnable或Callable任务给线程池执行。
    • 关闭线程池(通常使用shutdown()方法)。
  2. 示例代码(使用newSingleThreadExecutor创建一个单线程执行器):

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + " from MyRunnable");}
}public class SingleThreadExecutorTest {public static void main(String[] args) {ExecutorService executorService = Executors.newSingleThreadExecutor();MyRunnable myRunnable = new MyRunnable();for (int i = 0; i < 10; i++) {executorService.execute(myRunnable);}System.out.println("=======任务开始=======");executorService.shutdown();}
}

五、方法对比与选择

  • 继承Thread类:简单直观,但Java只支持单继承,如果类已经继承了其他类,则无法再继承Thread。
  • 实现Runnable接口:避免单继承的限制,更加灵活。Runnable接口还可以被共享,即多个线程可以共享一个Runnable实现类的实例。
  • 使用Callable和Future:比Runnable接口更强大,因为call()方法有返回值并且可以抛出异常。适用于需要返回结果的任务。
  • 使用线程池:提高性能,减少线程创建和销毁的开销。线程池还可以管理线程的并发数量和生命周期。

在选择线程创建方式时,应根据具体的应用场景和需求来决定。例如,对于简单的任务,可以直接继承Thread类或实现Runnable接口;对于需要返回结果的任务,可以使用Callable和Future;对于需要高性能和线程管理的场景,则应该使用线程池。

相关文章:

Java面试题五

一、谈谈你对Java内存模型&#xff08;JVM Memory Model&#xff09;的理解。 Java内存模型&#xff08;Java Memory Model&#xff0c;简称JMM&#xff09;是Java虚拟机&#xff08;JVM&#xff09;规范中定义的一种关于内存访问、共享变量在多线程之间的可见性、以及原子性、…...

2013 lost connection to MySQL server during query

1.问题 使用navicat连接doris&#xff0c;会有这个错误。 2.解决 换低版本的navicat比如navicat11。...

【英特尔IA-32架构软件开发者开发手册第3卷:系统编程指南】2001年版翻译,2-1

文件下载与邀请翻译者 学习英特尔开发手册&#xff0c;最好手里这个手册文件。原版是PDF文件。点击下方链接了解下载方法。 讲解下载英特尔开发手册的文章 翻译英特尔开发手册&#xff0c;会是一件耗时费力的工作。如果有愿意和我一起来做这件事的&#xff0c;那么&#xff…...

Android便携式WLAN热点分析

Android便携式WLAN热点分析 Platform: RK3368 OS: Android 6.0 Kernel: 3.10.0 文章目录 Android便携式WLAN热点分析分析Android开启WLAN热点的路由策略和防火墙便携式WLAN热点开启流程路由表路由策略iptables(防火墙) 用hostapd命令手动开启WLAN热点1.加载驱动2.配置wlan0 …...

从经济学原理看团队分工合作

最近我接触到一个经济学概念叫"比较优势"。学过经济学的同学应该很熟悉&#xff0c;但为了让不了解经济学的朋友们也能明白&#xff0c;我们先来解释下什么是比较优势&#xff0c;然后再谈谈为什么我认为这个原理应当被用在团队合作的分工当中。 比较优势是19世纪初…...

企业一级流程架构规划方法

在之前关于企业业务流程规划的系列文章中&#xff0c;我们已经分别对企业业务流程规划的价值和原则、企业的业务流程架构的应用、两种常见的企业总体业务流程架构模式等进行了比较深入的分析和阐述&#xff0c;相信大多数企业同仁&#xff0c;已经对企业的业务流程规划&#xf…...

用ESP32驱动stt7735 TFT屏幕

操作流程&#xff1a; 1 在esp32项目中搜索TFT库 2 找到对应TFT_eSPI库&#xff0c;添加进所在项目中&#xff08;这里可能下载很久&#xff09; 3 找到对应屏幕文件下的User_Setup.h 修改&#xff0c;将驱动换成ST7735_DRIVER&#xff0c;并修改对应屏幕引脚和屏幕尺寸&…...

Yolo目标检测:Yolo v1简介

一、Yolo系列介绍 YOLO&#xff08;You Only Look Once&#xff09;是一种基于深度学习的目标检测算法&#xff0c;由Joseph Redmon等人于2016年提出。它的核心思想是将目标检测问题转化为一个回归问题&#xff0c;通过一个神经网络直接预测目标的类别和位置。YOLO算法将输入图…...

Java動態轉發代理IP詳解

Java中實現動態轉發代理IP 在Java中實現動態轉發代理IP並不複雜&#xff0c;通常可以通過一些開源庫和框架來實現。下麵是一個簡單的實現思路&#xff1a; 設置HTTP請求&#xff1a;在Java中&#xff0c;可以使用HttpURLConnection或Apache HttpClient來發送HTTP請求。在發送…...

Android15之解决gdb:Remote register badly formatted问题(二百三十六)

简介&#xff1a; CSDN博客专家、《Android系统多媒体进阶实战》一书作者 新书发布&#xff1a;《Android系统多媒体进阶实战》&#x1f680; 优质专栏&#xff1a; Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a; 多媒体系统工程师系列【…...

【Router】路由器中NAT、NAPT、NPT是什么?

参考链接 NAT vs. NAPT: What’s the Difference? IPv6 Network Prefix Translation (NPt) | pfSense Documentation (netgate.com) 趣谈NAT/NAPT的原理&#xff0c;这篇不可不读&#xff01; - 知乎 (zhihu.com) NAT (Network Address Translation) NAT说明 NAT&#x…...

代码随想录算法训练营第三十九天|背包问题,416. 分割等和子集

背包问题&#xff0c;416. 分割等和子集 背包问题416. 分割等和子集 背包问题 有N件物品和一个最多能背重量为W 的背包。第i件物品的重量是weight[i]&#xff0c;得到的价值是value[i] 。每件物品只能用一次&#xff0c;求解将哪些物品装入背包里物品价值总和最大。 卡玛网的…...

可调用对象和Lambda

可调用对象&#xff1a; 函数 函数指针 函数对象 Lambda表达式(匿名函数) 01 函数对象 如果一个类实现了"函数调用运算符()"的重载&#xff0c;那么这个类的对象称为函数对象(仿函数) 函数对象的行为&#xff0c;类似于函数&#xff0c;可以被调用 #include …...

华为认证HCIE存储考啥?未来发展方向在哪?一个月就能轻松拿下?

说起HCIE&#xff0c;很多人第一反应都是路由交换、网络安全那些“热门”方向&#xff0c;而存储方向反而成了小众的存在。 其实&#xff0c;存储的江湖地位一点不低&#xff0c;尤其在数据爆炸的时代。 今天咱们就聊聊HCIE存储考什么、为什么要学&#xff0c;以及未来的可能…...

如何让自己的网站,被更多的人搜索到(免费方案)

文章目录 一、要做时间的朋友二、需要独立IP的服务器三、SEO信息如何设置设置网站TDK生成网站地图设置搜索引擎自动提交部署SSL证书加分项&#xff1a;定期更新文章 引言&#xff1a; 许多人都有这样一个问题&#xff1a;做好自己的网站&#xff0c;如何让这个网站被更多的人浏…...

Modbus 协议:工业自动化领域的通信脊梁

一、引言 在当今工业自动化的舞台上&#xff0c;数据的准确传输和设备间的有效通信是实现高效生产、精准控制的关键。Modbus 协议作为一种应用广泛、历史悠久的通信协议&#xff0c;在工业领域发挥着举足轻重的作用。从工厂的生产线到智能建筑的控制系统&#xff0c;从能源管理…...

函数的力量:掌握C语言的基石

目录 前言 标准库&#xff1a;C语言的百宝箱 头文件&#xff1a;库函数的藏宝图 实例分析&#xff1a;计算平方根的sqrt函数 功能描述 头文件包含的重要性 库函数文档的一般格式 自定义函数&#xff1a;释放你的编程创造力 函数的语法形式 函数的比喻 函数的举例 简化…...

U-Boot的移植流程

U-Boot的简化版启动流程&#xff1a; 1、设置状态寄存器 cpsr &#xff0c;使CPU进入 SVC 特权模式&#xff0c;并且禁止 FIQ 和 IRQ&#xff1b; 2、关闭看门狗、中断、MMU、Cache&#xff1b; 3、初始化部分寄存器和外设&#xff08;时钟、串口、Flash、内存&#xff09;&…...

xRDP – 在 Ubuntu 18.04、20.04、22.04、22.10、23.04(脚本版本 1.4.7)上轻松安装 xRDP

最新脚本Repository | c-nergy.be 概述 到目前为止&#xff0c;您应该知道 xrdp-installer 脚本旨在简化 xRDP 在 Ubuntu 操作系统上的安装和配置后操作。xRDP 是一款在 Linux 上启用远程桌面服务的软件。这意味着 Windows 用户可以使用他们的远程桌面客户端 &#xff08;mst…...

[Linux网络编程]04-多进程/多线程并发服务器思路分析及实现(进程,信号,socket,线程...)

一.思路 实现一个服务器可以连接多个客户端&#xff0c;每当accept函数等待到客户端进行连接时 就创建一个子进程; 核心思路&#xff1a;让accept循环阻塞等待客户端&#xff0c;每当有客户端连接时就fork子进程&#xff0c;让子进程去和客户端进行通信&#xff0c;父进程用于…...

《OpenCV计算机视觉》—— 年龄与性别预测

结合以下链接中的文章有助于理解此篇案例&#xff1a; OpenCV中的 cnn 模块 https://blog.csdn.net/weixin_73504499/article/details/142965441?spm1001.2014.3001.5501 此案例是通过使用OpenCV中的cnn模块来调用别人已经训练好的深度学习模型&#xff0c;此篇案例中用到了…...

详解23种设计模式——第一部分:概述+创建型模式

目录 1. 概述 2. 创建型模式 2.1 简单&#xff08;静态&#xff09;工厂模式 2.1.1 介绍 2.1.2 实现 2.2 工厂模式 2.3 抽象工厂模式 2.4 单例模式 2.4.1 饿汉模式 2.4.2 懒汉模式 2.4.3 线程安全的懒汉式 2.4.4 DCL单例 - 高性能的懒汉式 2.5 建造者模式 2.6 原…...

semi-Naive Bayesian(半朴素贝叶斯)

semi-Naive Bayesian&#xff08;半朴素贝叶斯&#xff09; 引言 朴素贝叶斯算法是基于特征是相互独立这个假设开展的&#xff08;为了降低贝叶斯公式: P ( c ∣ x ) P ( c ) P ( x ∣ c ) P ( x ) P(c|x) \frac {P(c)P(x|c)}{P(x)} P(c∣x)P(x)P(c)P(x∣c)​中后验概率 P …...

大语言模型(LLM)入门级选手初学教程

链接&#xff1a;https://llmbook-zh.github.io/ 前言&#xff1a; GPT发展&#xff1a;GPT-1 2018 -->GPT-2&GPT-3&#xff08;扩大预训练数据和模型参数规模&#xff09;–> GPT-3.5&#xff08;代码训练、人类对齐、工具使用等&#xff09;–> 2022.11 ChatG…...

HTML 实例/测验之HTML 基础一口气讲完!(o-ωq)).oO 困

HTML 基础 非常简单的HTML文档 <!DOCTYPE html> <html><head><title>页面标题(w3cschool.cn)</title></head><body><h1>我的第一个标题</h1><p>我的第一个段落。</p></body> </html> 输出&a…...

c语言基础程序——经典100道实例。

c语言基础程序——经典100道实例 001&#xff0c; 组无重复数字的数002&#xff0c;企业发放的奖金根据利润提成003&#xff0c;完全平方数004&#xff0c;判断当天是这一年的第几天005&#xff0c;三个数由小到大输出006&#xff0c;输出字母C图案007&#xff0c;特殊图案008&…...

火星求生CE修改金钱,无限资金

由于火星求生前期没有资金非常难玩&#xff0c;想通过修改资金渡过前期&#xff0c;网上找了一圈修改器&#xff0c;只有修改无限声望和无限科研&#xff0c;就是没有无限资金&#xff0c;于是自己用CE修改 教程 首先查看自己资金是多少M&#xff0c;如下图我是22430M资金&…...

linux 内存管理-slab分配器

伙伴系统用于分配以page为单位的内存,在实际中很多内存需求是以Byte为单位的,如果需要分配以Byte为单位的小内存块时,该如何分配呢? slab分配器就是用来解决小内存块分配问题,也是内存分配中非常重要的角色之一。 slab分配器最终还是由伙伴系统分配出实际的物理内存,只不过s…...

docker-compose部署gitlab(亲测有效)

一.通过DockerHub拉取Gitlab镜像 docker pull gitlab/gitlab-ce:latest 二.创建目录 mkdir -p /root/tool/gitlab/{data,logs,config} && cd /root/tool/gitlab/ 三.编辑DockerCompose.yaml文件 vim /root/tool/gitlab/docker-compose.yml version: "3&quo…...

Leetcode 赎金信

利用hash map做 java solution class Solution {public boolean canConstruct(String ransomNote, String magazine) {//首先利用HashMap统计magazine中字符频率HashMap<Character, Integer> magazinefreq new HashMap<>();for(char c : magazine.toCharArray())…...