多线程经典面试题总结

1.什么是线程

  • 线程是程序执行的一条路径,一个进程中可以包含多条线程
  • 多线程并发执行可以提高程序的效率,可以同时完成多项工作

2.多线程的应用场景

  • 红蜘蛛同时共享屏幕给多个电脑
  • 迅雷开启多条线程一起下载
  • QQ同时和多个人一起视频
  • 服务器同时处理多个客户端请求

3.多线程并行和并发的区别

  • 并行就是两个任务同时运行,就是甲任务进行的同时,乙任务也在进行(需要多核CPU)
  • 并发是指两个任务都请求运行,而处理器只能接受一个任务,就把这两个任务安排轮流进行,由于时间间隔较短,
    使人感觉两个任务都在运行
  • 举例:我跟两个网友聊天,左手操作一个电脑跟甲聊,同时右手用另一台电脑跟乙聊天,这就叫并行
  • 如果用一台电脑我先给甲发个消息,然后立刻再给乙发消息,然后再跟甲聊,再跟乙聊,这就叫并发

4.Java程序运行原理和JVM的启动是多线程的吗

  • A:Java程序运行原理:

    • Java命令会启动java虚拟机,启动JVM等于启动了一个应用程序,也就是启动了一个进程,
      该进程会自动启动一个"主线程",然后主线程去调用某个类的main方法
  • B:JVM的启动是多线程的吗

    • JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的

5.多线程程序实现的方式

1.继承Thread
  • 定义类继承Thread
  • 重写run方法
  • 把新线程要做的事写在run方法中
  • 创建线程对象
  • 开启新线程,内部会自动执行run方法
2.实现Runnable
  • 定义类实现Runnable接口
  • 实现run方法
  • 把新线程要做的事写在run方法中
  • 创建自定义的Runnable的子类对象
  • 创建Thread对象,传入Runnable
  • 调用start()开启新线程,内部会自动调用Runnable的run()方法
  • a.继承Thread:由于子类重写了Thread类的run(),当调用start()时,直接找子类的run()方法
  • b.实现Runnable:构造函数中传入了Runnable的引用,成员变量记住了它,start()调用run()方法时,
    内部判断成员变量Runnable的引用是否为空,不为空编译时看的是Runnable接口的run()方法,
    运行时执行的是接口实现类的run()方法
3.实现Callable接口,重写call函数

​ Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务。

6.Thread和Runnable两种方式的区别

  • 继承Thread
    • 好处是:可以直接使用Thread类中的方法,代码简单
    • 弊端是:如果已经有了父类,就不能用这种方法
  • 实现Runnable接口
    • 好处是:即使自己定义的线程类有了父类也没关系,因为有了父类也可以实现接口,而且接口是可以多实现的
    • 弊端是:不能直接使用Thread中的方法需要先获取到线程对象后,才能得到Thread的方法,代码相对复杂

7.获取名字和设置名字

  • 1.获取名字:
    • 通过getName()方法获取线程对象的名字//默认从Thread-0开始命名,下一个为Thread-1
  • 2.设置名字:
    • 通过构造函数可以传入String类型的名字:,或者通过setName(String)方法可以设置线程对象的名字

获取当前线程的对象
Thread.currentThread(),主线程也可以获取

8.同步代码块

  • 1.什么情况下需要同步:
    • 当多线程并发,有多段代码同时执行时,我们希望某一段代码执行的过程中,CPU不要切换到其他线程工作,
      这时就需要同步,
    • 如果两段代码是同步的,那么同一时间只能执行一段,在一段代码没执行结束之前,不会执行另外一段代码
  • 2.同步代码块:
    • 使用synchronized关键字加上一个锁对象来定义一段代码,这就叫同步代码块,
    • 多个同步代码块,如果使用相同的锁对象,那么他们就是同步的,这个要特别注意,

9.同步方法

  • 使用synchronized关键字修饰一个方法,该方法中所有的代码都是同步的
  • 非静态的同步方法的锁对象是this,静态的同步方法的锁对象是该类的字节码对象

10.线程安全问题

  • 多线程并发操作同一数据时,就有可能出现线程安全问题,如出现数据丢失,读写异常的情况
  • 使用同步技术可以解决这种问题,把操作数据的代码进行同步,不要多个线程一起操作

用同步代码块时要保证是同一把锁,如果用继承线程类创建四个线程时this对象都不同,而用实现Runnable接口时,虽然
也创建了四个线程类对象,但是传入构造的都是同一个Runnable对象引用即this,所以为了避免这种麻烦,以后用同步代码块时,
锁对象最好用同步代码块所在类的字节码对象,这样可以保证是同一把锁,或者在同步代码块所在类定义一个静态成员变量,如
private static Object obj = new Object();用obj作为锁,但是麻烦,所以,统一用同步代码块所在类的字节码对象做同步锁
但是,如果用的是同步方法就要分情况了,非静态同步方法的锁对象是this,而静态同步方法的锁对象是该类的字节码对象!!!

11.为什么wait方法和notify方法定义在Object这类中

因为锁对象可以是任意对象,Object是所有的类的基类,所以wait方法和notify方法需要定义在Object这个类中

12.sleep方法和wait方法的区别

1.每个对象都有一个锁来控制同步访问。Synchronized关键字可以和对象的锁交互,来实现线程的同步。
sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。
2.wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用
3.sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常

4.sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。

5.wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。

13.线程的六种状态

  1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。

  2. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。

    线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。

  3. 阻塞(BLOCKED):表示线程阻塞于锁。

  4. 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。

  5. 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。

  6. 终止(TERMINATED):表示该线程已经执行完毕。

*多线程在项目中的应用

​ 很多面试官都会问多线程在项目中的实际应用场景,这个时候我们通常不知道如何回答。因为我们大多数程序员通常都是和业务代码打交道,需要用到多线程的地方我们容器和框架一般都替我们处理好了,所以我们很少有机会接触到多线程编程。但是实际上依然还有一些场景可以使用多线程来处理,这里我列举一下在实际项目中用到的多线程:

​ 1.批量页面静态化在系统中,商品详情页我们使用freemarker来进行页面静态化,每天夜里12点开始要对所有商品页面进行一遍静态化,由于商品数量比较多 如果使用单线程将耗时过长,我们使用一个定长线程池进行批量执行,将任务放在队列中,多个线程同时领取并执行;

​ 2.订单处理(用户下单后可能支付状态不明确,我们后台可以通过多线程去主动核实第三方支付状态,来更新我们系统的订单状态);
​ 3.登录后用户信息处理(用户登录后应该通知各相关系统将用户常用数据进行缓存 以快速响应登录用户);

​ 该问题需要大家在后面实战项目学习中自己思索!!!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值