常见面试题之线程基础知识

news2025/2/26 23:06:27

1. 线程和进程的区别?

程序由指令和数据组成,但这些指令要运行,数据要读写,就必须将指令加载至CPU,数据加载至内存。在指令运行过程中还需要用到磁盘、网络等设备。进程就是用来加载指令、管理内存、管理IO的。

当一个程序被运行,从磁盘加载这个程序的代码至内存,这时就开启了一个进程。

一个进程之内可以分为一到多个线程。

一个线程就是一个指令流,将指令流中的一条条指令以一定的顺序交给CPU执行。

Java中,线程作为最小调度单位,进程作为资源分配的最小单位。在windows中进程是不活动的,只是作为线程的容器。

在这里插入图片描述

二者对比

  • 进程是正在运行程序的实例,进程中包含了线程,每个线程执行不同的任务;
  • 不同的进程使用不同的内存空间,在当前进程下的所有线程可以共享内存空间;
  • 线程更轻量,线程上下文切换成本一般上要比进程上下文切换低(上下文切换指的是从一个线程切换到另一个线程)。

2. 并行和并发有什么区别?

单核CPU

  • 单核CPU下线程实际还是串行执行的。

  • 操作系统中有一个组件叫做任务调度器,将cpu的时间片(windows下时间片最小约为 15 毫秒)分给不同的程序使用,只是由于cpu在线程间(时间片很短)的切换非常快,人类感觉是同时运行的 。

  • 总结为一句话就是: 微观串行,宏观并行。

一般会将这种线程轮流使用CPU的做法称为并发(concurrent)。

在这里插入图片描述

在这里插入图片描述

多核CPU

每个核(core)都可以调度运行线程,这时候线程可以是并行的。

在这里插入图片描述

并发(concurrent)是同一时间应对(dealing with)多件事情的能力。

并行(parallel)是同一时间动手做(doing)多件事情的能力。

举例:

  • 家庭主妇做饭、打扫卫生、给孩子喂奶,她一个人轮流交替做这多件事,这时就是并发

  • 家庭主妇雇了个保姆,她们一起这些事,这时既有并发,也有并行(这时会产生竞争,例如锅只有一口,一个人用锅时,另一个人就得等待)

  • 雇了3个保姆,一个专做饭、一个专打扫卫生、一个专喂奶,互不干扰,这时是并行

3. 创建线程的四种方式?

共有四种方式可以创建线程,分别是:继承Thread类、实现runnable接口、实现Callable接口、线程池创建线程。

详细创建方式参考下面代码:

继承Thread

public class MyThread extends Thread {

    @Override
    public void run() {
        System.out.println("MyThread...run...");
    }

    
    public static void main(String[] args) {

        // 创建MyThread对象
        MyThread t1 = new MyThread() ;
        MyThread t2 = new MyThread() ;

        // 调用start方法启动线程
        t1.start();
        t2.start();

    }
    
}

实现runnable接口

public class MyRunnable implements Runnable{

    @Override
    public void run() {
        System.out.println("MyRunnable...run...");
    }

    public static void main(String[] args) {

        // 创建MyRunnable对象
        MyRunnable mr = new MyRunnable() ;

        // 创建Thread对象
        Thread t1 = new Thread(mr) ;
        Thread t2 = new Thread(mr) ;

        // 调用start方法启动线程
        t1.start();
        t2.start();

    }

}

实现Callable接口

public class MyCallable implements Callable<String> {

    @Override
    public String call() throws Exception {
        System.out.println("MyCallable...call...");
        return "OK";
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        // 创建MyCallable对象
        MyCallable mc = new MyCallable() ;

        // 创建F
        FutureTask<String> ft = new FutureTask<String>(mc) ;

        // 创建Thread对象
        Thread t1 = new Thread(ft) ;
        Thread t2 = new Thread(ft) ;

        // 调用start方法启动线程
        t1.start();

        // 调用ft的get方法获取执行结果
        String result = ft.get();

        // 输出
        System.out.println(result);

    }

}

线程池创建线程

public class MyExecutors implements Runnable{

    @Override
    public void run() {
        System.out.println("MyRunnable...run...");
    }

    public static void main(String[] args) {

        // 创建线程池对象
        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        threadPool.submit(new MyExecutors()) ;

        // 关闭线程池
        threadPool.shutdown();

    }

}

4. runnablecallable有什么区别?

  1. Runnable接口run方法没有返回值;Callable接口call方法有返回值,是个泛型,和FutureFutureTask配合可以用来获取异步执行的结果。
  2. Callalbe接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。
  3. Callable接口的call()方法允许抛出异常;而Runnable接口的run()方法的异常只能在内部消化,不能继续上抛。

5. 线程的run()start()有什么区别?

start(): 用来启动线程,通过该线程调用run方法执行run方法中所定义的逻辑代码。start方法只能被调用一次。

run(): 封装了要被线程执行的代码,可以被调用多次。

6. 线程包括哪些状态,状态之间是如何变化的?

线程的状态可以参考JDK中的Thread类中的枚举State

public enum State {
        /**
         * 尚未启动的线程的线程状态
         */
        NEW,

        /**
         * 可运行线程的线程状态。处于可运行状态的线程正在 Java 虚拟机中执行,但它可能正在等待来自		 * 操作系统的其他资源,例如处理器。
         */
        RUNNABLE,

        /**
         * 线程阻塞等待监视器锁的线程状态。处于阻塞状态的线程正在等待监视器锁进入同步块/方法或在调          * 用Object.wait后重新进入同步块/方法。
         */
        BLOCKED,

        /**
         * 等待线程的线程状态。由于调用以下方法之一,线程处于等待状态:
		* Object.wait没有超时
         * 没有超时的Thread.join
         * LockSupport.park
         * 处于等待状态的线程正在等待另一个线程执行特定操作。
         * 例如,一个对对象调用Object.wait()的线程正在等待另一个线程对该对象调用Object.notify()			* 或Object.notifyAll() 。已调用Thread.join()的线程正在等待指定线程终止。
         */
        WAITING,

        /**
         * 具有指定等待时间的等待线程的线程状态。由于以指定的正等待时间调用以下方法之一,线程处于定          * 时等待状态:
		* Thread.sleep
		* Object.wait超时
		* Thread.join超时
		* LockSupport.parkNanos
		* LockSupport.parkUntil
         * </ul>
         */
        TIMED_WAITING,

        /**
         * 已终止线程的线程状态。线程已完成执行
         */
        TERMINATED;
    }

状态之间是如何变化的。

在这里插入图片描述

分别是:

  • 新建
    • 当一个线程对象被创建,但还未调用start方法时处于新建状态;
    • 此时未与操作系统底层线程关联;
  • 可运行
    • 调用了start方法,就会由新建进入可运行
    • 此时与底层线程关联,由操作系统调度执行;
  • 终结
    • 线程内代码已经执行完毕,由可运行进入终结
    • 此时会取消与底层线程关联;
  • 阻塞
    • 当获取锁失败后,由可运行进入Monitor的阻塞队列阻塞,此时不占用cpu时间;
    • 当持锁线程释放锁时,会按照一定规则唤醒阻塞队列中的阻塞线程,唤醒后的线程进入可运行状态;
  • 等待
    • 当获取锁成功后,但由于条件不满足,调用了wait()方法,此时从可运行状态释放锁进入Monitor等待集合等待,同样不占用cpu时间;
    • 当其它持锁线程调用notify()notifyAll()方法,会按照一定规则唤醒等待集合中的等待线程,恢复为可运行状态;
  • 有时限等待
    • 当获取锁成功后,但由于条件不满足,调用了wait(long)方法,此时从可运行状态释放锁进入 Monitor等待集合进行有时限等待,同样不占用cpu时间;
    • 当其它持锁线程调用notify()notifyAll()方法,会按照一定规则唤醒等待集合中的有时限等待线程,恢复为可运行状态,并重新去竞争锁;
    • 如果等待超时,也会从有时限等待状态恢复为可运行状态,并重新去竞争锁;
    • 还有一种情况是调用sleep(long)方法也会从可运行状态进入有时限等待状态,但与Monitor无关,不需要主动唤醒,超时时间到自然恢复为可运行状态;

7. 新建T1T2T3三个线程,如何保证它们按顺序执行?

在多线程中有多种方法让线程按特定顺序执行,你可以用线程类的**join()**方法在一个线程中启动另一个线程,另外一个线程完成该线程继续执行。

代码举例:

为了确保三个线程的顺序你应该先启动最后一个(T3调用T2T2调用T1),这样T1就会先完成而T3最后完成。

public class JoinTest {

    public static void main(String[] args) {

        // 创建线程对象
        Thread t1 = new Thread(() -> {
            System.out.println("t1");
        }) ;

        Thread t2 = new Thread(() -> {
            try {
                t1.join();                          // 加入线程t1,只有t1线程执行完毕以后,再次执行该线程
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t2");
        }) ;


        Thread t3 = new Thread(() -> {
            try {
                t2.join();                              // 加入线程t2,只有t2线程执行完毕以后,再次执行该线程
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t3");
        }) ;

        // 启动线程
        t1.start();
        t2.start();
        t3.start();

    }

}

8. notify()notifyAll()有什么区别?

notifyAll:唤醒所有wait的线程;

notify:只随机唤醒一个wait线程;

package com.dcxuexi.basic;

public class WaitNotify {

    static boolean flag = false;
    static Object lock = new Object();

    public static void main(String[] args) {

        Thread t1 = new Thread(() -> {
            synchronized (lock){
                while (!flag){
                    System.out.println(Thread.currentThread().getName()+"...wating...");
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName()+"...flag is true");
            }
        });

        Thread t2 = new Thread(() -> {
            synchronized (lock){
                while (!flag){
                    System.out.println(Thread.currentThread().getName()+"...wating...");
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName()+"...flag is true");
            }
        });

        Thread t3 = new Thread(() -> {
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName() + " hold lock");
                lock.notifyAll();
                flag = true;
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t1.start();
        t2.start();
        t3.start();

    }

}

9. 在javawaitsleep方法的不同?

共同点:

  • wait()wait(long)sleep(long)的效果都是让当前线程暂时放弃CPU的使用权,进入阻塞状态;

不同点:

  • 方法归属不同

    • sleep(long)Thread的静态方法;
    • wait()wait(long)都是Object的成员方法,每个对象都有;
  • 醒来时机不同

    • 执行sleep(long)wait(long)的线程都会在等待相应毫秒后醒来;
    • wait(long)wait()还可以被notify唤醒,wait()如果不唤醒就一直等下去;
    • 它们都可以被打断唤醒
  • 锁特性不同(重点)

    • wait方法的调用必须先获取wait对象的锁,而sleep则无此限制;
    • wait方法执行后会释放对象锁,允许其它线程获得该对象锁(我放弃cpu,但你们还可以用);
    • sleep如果在synchronized代码块中执行,并不会释放对象锁(我放弃cpu,你们也用不了);

代码示例:

public class WaitSleepCase {

    static final Object LOCK = new Object();

    public static void main(String[] args) throws InterruptedException {
        sleeping();
    }

    private static void illegalWait() throws InterruptedException {
        LOCK.wait();
    }

    private static void waiting() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            synchronized (LOCK) {
                try {
                    get("t").debug("waiting...");
                    LOCK.wait(5000L);
                } catch (InterruptedException e) {
                    get("t").debug("interrupted...");
                    e.printStackTrace();
                }
            }
        }, "t1");
        t1.start();

        Thread.sleep(100);
        synchronized (LOCK) {
            main.debug("other...");
        }

    }

    private static void sleeping() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            synchronized (LOCK) {
                try {
                    get("t").debug("sleeping...");
                    Thread.sleep(5000L);
                } catch (InterruptedException e) {
                    get("t").debug("interrupted...");
                    e.printStackTrace();
                }
            }
        }, "t1");
        t1.start();

        Thread.sleep(100);
        synchronized (LOCK) {
            main.debug("other...");
        }
    }
}

10. 如何停止一个正在运行的线程?

有三种方式可以停止线程:

  • 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止;
  • 使用stop方法强行终止(不推荐,方法已作废);
  • 使用interrupt方法中断线程;

代码参考如下:

使用退出标志,使线程正常退出

public class MyInterrupt1 extends Thread {

    volatile boolean flag = false ;     // 线程执行的退出标记

    @Override
    public void run() {
        while(!flag) {
            System.out.println("MyThread...run...");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {

        // 创建MyThread对象
        MyInterrupt1 t1 = new MyInterrupt1() ;
        t1.start();

        // 主线程休眠6秒
        Thread.sleep(6000);

        // 更改标记为true
        t1.flag = true ;

    }
}

使用stop方法强行终止

public class MyInterrupt2 extends Thread {

    volatile boolean flag = false ;     // 线程执行的退出标记

    @Override
    public void run() {
        while(!flag) {
            System.out.println("MyThread...run...");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {

        // 创建MyThread对象
        MyInterrupt2 t1 = new MyInterrupt2() ;
        t1.start();

        // 主线程休眠2秒
        Thread.sleep(6000);

        // 调用stop方法
        t1.stop();

    }
}

使用interrupt方法中断线程

package com.dcxuexi.basic;

public class MyInterrupt3 {

    public static void main(String[] args) throws InterruptedException {

        //1.打断阻塞的线程
        /*Thread t1 = new Thread(()->{
            System.out.println("t1 正在运行...");
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t1");
        t1.start();
        Thread.sleep(500);
        t1.interrupt();
        System.out.println(t1.isInterrupted());*/


        //2.打断正常的线程
        Thread t2 = new Thread(()->{
            while(true) {
                Thread current = Thread.currentThread();
                boolean interrupted = current.isInterrupted();
                if(interrupted) {
                    System.out.println("打断状态:"+interrupted);
                    break;
                }
            }
        }, "t2");
        t2.start();
        Thread.sleep(500);
//        t2.interrupt();

    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/679290.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DataStructure01|ArrayList和顺序表

ArrayList与顺序表 1.线性表 ​ 线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有限序列。线性表是一种在实际中广泛使用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列… ​ 线性表在逻辑上是线性结构&#xff0c;也就说…

产品设计.从用户体验五要素出发,谈如何设计产品

用户调研--产品定位---产品方案---视觉设计 作者 | 渐渐见减减简https://www.zcool.com.cn/article/ZMTEyNDA2NA.html 用户体验五要素是一种产品分析与设计的方法论&#xff0c;帮助我们以正确方式从0到1设计一款产品。 1 战略层 企业做一个产品前&#xff0c;都要明确几个问题…

多目标樽海鞘算法MATLAB实战(附源码)

今天给大家分享多目标樽海鞘算法&#xff0c;主要从算法原理和代码实战展开。需要了解智能算法、机器学习、深度学习和信号处理相关理论的可以后台私信哦&#xff0c;下一期分享的内容就是你想了解的内容。 一、算法原理 上一篇分享的SSA算法能够驱动salps向食物来源靠近&…

【Flutter】包管理(6)Flutter 状态管理 Provider 深入使用指南

文章目录 一、 前言二、 ProxyProvider 的使用三、 处理异步数据四、 性能优化五、 版本信息六、 总结一、 前言 在我们的上一篇文章中,我们介绍了 Flutter 中 Provider 包的基本使用。 在这篇文章中,我们将深入探讨 Provider 的高级使用方法,包括如何使用 ProxyProvider,…

Green板 和Red板和Nv EVM板比较

001 电源开关、 复位、烧写按钮 G&#xff1a;绿板 K3: Reset press RN K1: Brush RECOVERY button K2: Start button Power ON R: 红板 K3: POWER_KEY K2: FORCE_RECOVERY K1: RESET_KEY 002 USB设计 烧写连接器 G: 绿板 J6&#xff1a;TYPE C 烧写连接器 USB0_DP …

streamlit——搭建作业、文件上传网站(代码编写、服务器部署)

使用streamlit搭建简单的作业提交网站 文章目录 使用streamlit搭建简单的作业提交网站一、引言二、streamlit代码三、搭建到服务器中1、放行安全组2、将代码放到服务器中3、配置依赖4、运行代码 一、引言 由于我平时需要收集各种类型的作业文件。但传统的微信收文件方式很让我…

PyQt5中文手册

PyQt5中文手册 一、介绍 本教程的目的是带领你入门PyQt5。教程内所有代码都在Linux上测试通过。PyQt4 教程是PyQt4的教程&#xff0c;PyQt4是一个Python&#xff08;同时支持2和3&#xff09;版的Qt库。 关于 PyQt5 PyQt5 是Digia的一套Qt5应用框架与python的结合&#xff…

实训五:数据库安全控制 - 授权及回收权限

授权及回收权限 第1关&#xff1a;授权任务描述相关知识MySql系统库中的权限表访问控制的两个阶段&#xff1a;授予的权限等级&#xff1a;MySQL 权限类型grant 语句的基本语法格式revoke 语句的基本语法格式查看权限限制权限 编程要求测试说明参考代码 第2关&#xff1a;授权-…

论文悦读(5)——NVM文件系统之CtFS(FAST‘22)文件系统

CtFS&#xff08;FAST22&#xff09; 1. 背景&#xff08;Background&#xff09;1.1 NVM1.2 NVM文件系统1.3 快速索引方案 2. 观察与动机&#xff08;Observation & Motivation&#xff09;3. CtFS设计与实现&#xff08;Design & Implementation&#xff09;3.1 系统…

ASP.NET Dotnet 3.5实验室LIMS系统源码

LIMS系统功能包括&#xff1a;检测管理&#xff08;合同管理、样品管理、样品收发管理、工作任务分配、检测结果登记、复核及审核、留样管理等&#xff09;、报告管理&#xff08;报告编制、审核、签发、打印等&#xff09;、原始记录管理、仪器设备管理、消耗品管理、文件管理…

Oralce系列十九:Oralce的体系结构

Oralce的体系结构 1. 物理结构2. 内存结构2.1 SGA2.2 后台进程 3. 逻辑结构 1. 物理结构 Oracle数据库的物理结构由参数文件、控制文件、数据文件和日志文件组成&#xff0c;用于存储和管理数据库的数据和元数据&#xff0c;每个文件都扮演着不可或缺的角色。 参数文件用于配…

盘点那些VS Code上的常用插件

插件可以使代码开发过程变得更加轻松和高效 文章目录 前言中文版主题插件显示错误游戏插件连接数据库拼写检查插件前端智能提示刷算法题的插件美化注释的插件美化括号的插件自动补全路径和文件名在浏览器中打开 HTML 文件在 VS Code 里面使用 Postman同步 VS Code 配置的插件 前…

MFC第七天 单机版数据库增删改的方法和用户登录(密码修改)、权限管理功能的员工管理系统的应用 以及 CCombox类的属性和方法

文章目录 员工管理系统的应用对TCHAR类型简介&#xff1a;Unicode软件开发中的文字转换方式有三种 CComboBox控件属性和类库介绍CBS_组合框控件属性CComboBox常用方法 员工管理系统示例代码如下Employer_sql.hEmployer_sql.cppMainDlg.hMainDlg.cpp: 实现文件CLoginDlg.hCLogin…

入坑 TS(一)

1、TS 采用类型后置的语法 const str: string "我是字符串"; const num: number "我是数值"; const bool: boolean false;函数怎么写&#xff1f; // 有返回值 function sayHello(str: string): string {return str; } // 无返回值 function sayHello…

Java throws关键字

Java的throws关键字用于声明异常。它向程序员提供了可能发生异常的信息。 因此&#xff0c;最好为程序员提供异常处理代码&#xff0c;以便保持程序的正常流程。 异常处理主要用于处理已检查异常。如果出现任何未检查的异常&#xff0c;例如NullPointerException&#xff0c;则…

jjy-shareV2.0一键分享到微博,QQ空间,QQ好友,微信

介绍 jjy-shareV2.0一键分享到微博&#xff0c;QQ空间&#xff0c;QQ好友&#xff0c;微信 作者&#xff1a;极纪元-橙 作者博客&#xff1a;https://cplvfx.blog.csdn.net/ 码云Gitee&#xff1a;橙cplvfx极纪元 (cplvfx) - Gitee.com 项目地址&#xff1a;jjy-shareV2.…

登录认证(全集)--jwt,Filter,Interceptor详细说明

一、登录校验 问题&#xff1a;在未登录情况下&#xff0c;我们也可以直接访问部门管理、员工管理等功能。由于浏览器与web服务器中的数据交互是通过HTTP协议的&#xff0c;而HTTP协议是无状态的–即每个页面中的请求和响应都是独立的&#xff0c;没有状态存在。所以我们需要进…

WordPress网站的robots文件代码怎样写比较好

本文记载和介绍的是wordpress的robots.txt的在哪儿&#xff1f;以及如何修改robots文件来禁止搜索引擎爬取/ajax目录下的文章。以及如何优化WordPress网站的robots文件 wordpress设置robots文件的目录&#xff1a;/wp-includes/functions.php&#xff0c;然后搜索do_robotstxt…

【雕爷学编程】Arduino动手做(123)---Multi-function扩展板

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

随机数检测(一)

随机数检测&#xff08;一&#xff09;- 随机数自测试概述 1 概述2 产品形态划分和检测项目3 测试方式3.1 概述3.2 单比特频数检测方法3.3块内频数检测方法 1 概述 随机数发生器设计完成后&#xff0c;使用随机数发生器的产品需对其执行测试&#xff0c;防止应用过程中产生不合…