【Java多线程】面试题

文章目录

线程相关

创建线程的几种方式

四种方式

  1. 实例化Thread
  2. 实现Runnable
  3. Callable与FutureTask结合使用
  4. 线程池

详细实现参考:https://editor.csdn.net/md/?articleId=131819983

Runnable和Callable的区别

  1. 最大的区别,runnable没有返回值,而实现callable接口的任务线程能返回执行结果
  2. callable接口实现类中的run方法允许异常向上抛出,可以在内部处理,try catch,但是runnable接口实现类中run方法的异常必须在内部处理,不能抛出

如何启动一个线程,调用Start和run方法的区别

启动线程:
调用线程的thread.start()方法
start和run方法的区别:

	实现多线程最基本的两种方式:
		(1) java.lang.Thread 接口
	  	(2) java.lang.Runnable 接口
	其中Thread也是实现了Runnable接口

Runnable接口源码

@FunctionalInterface
public interface Runnable {
    /**
     * 当使用实现接口Runnable的对象来创建线程时,启动线程会导致在单独执行的线程中调用对象的run方法。方法运行的一般约定是它可以执行任何操作。
     */
    public abstract void run();
}

所以创建多线程都需要实现run()方法,是多线程真正执行的主方法
而start()方法则是Thread类的方法,用来异步启动一个线程,然后主线程立刻返回,该启动的线程不会马上运行,会放到等待队列中等待CPU调度,只有线程真正被CPU调用时才会调用run()方法执行,所以start()方法只是标识线程为就绪状态的一个附加方法,其中start()是一个本地native()方法
直接调用线程的run()方法 ,相当于调用了一个普通的同步方法。

线程有哪几种状态以及各种状态之间的转换

详解参考:https://editor.csdn.net/md/?articleId=131829207

线程相关的基本方法

sleep和wait的区别

  1. 使用限制

     sleep(long)在线程的任何地方都能使用、
     wait() 、wait(long) 需要配合synchronized使用
     wait() 唤醒需要配合notify() 或者 notifyAll() 使用
    
  2. 不同的类

     sleep(long) 在 Thread类中,wait()、wait(long)在Object类中
    
  3. 使用场景

     sleep() 一般用户当前线程休眠,wait() 用于多线程之间的通信
    
  4. 释放锁

     wait()会释放获取到的锁并允许其他线程获取锁并继续执行,而sleep() 不会
    
  5. 线程切换(CPU资源)

     sleep()会让出CUP时间片,强制上下文切换
     wait()会让出CPU时间片,但是不会强制上下文切换
    

yield和sleep的区别

相同点:

	1. 暂停当前线程
	2. 如果获取到锁资源,都不会释放锁
	3. 都会让出CPU时间片资源

不同点:

	1. sleep() 可以指定休眠时间,而yield则依赖CPU的时间片划分
	2. yield不能被中断,而sleep则可以被中断(需要捕获中断异常)
	3. sleep() 让出CPU时间片给其他线程运行的机会不会考虑优先级,
	   而yield() 只会给相同优先级或者更高优先级的线程运行的机会。
	5. sleep()方法声明抛出InterruptedException异常,yield没有

wait、notify、notifyAll()并不是Thread类中的方法,而是Object类中的,为什么呢?

	因为sleep(long)是让当前线程休眠,
	不涉及到对象类,也不需要获取对象的锁,
	所以是线程类方法。wait()是让获得对象锁的线程实现等待,
	前提是获取到对象的锁,所以是类的方法。notify、notifyAll()与wait配套使用,
	所以也是Object类中的方法。

为什么不推荐使用Stop停止线程

stop()会释放锁并强制终止线程,造成执行一半的线程终止。
假如业务场景:

	一个线程正在处理一个复制的业务流程,突然间线程被调用Stop而意外终止,这个业务数据就可能出现数据不一致的问题。

正因为stop方法太过暴力,所以一般不推荐使用,除非使用stop方法不会对业务带来影响

如何优化的终止一个线程

标志位
如何实现

	添加一个volatile变量,判断这个变量在某个值的时候退出循环。
public class TaskThread extends Thread {
	// 为什么需要使用volatile 关键字 参考volatile 详解
	private static volatile boolean stop = false;
	
	@Override
	publlic void run() {
		while(!stop) {
			// 线程逻辑
		}
	}
	
	public static void main(String[] args) throws InterruptedException {
		TaskThread thread = new TaskThread();
		thread.start();
		TimeUnit.SECONDS.sleep(3);
		stop = true;
	}
}

如何实现线程顺序执行

详解参考:https://blog.csdn.net/ChrisLee2013/article/details/131841698

synchronized同步锁有哪几种用法

实例锁

  1. 同步普通方法
  2. 同步this实例
    类锁
  3. 同步静态方法
  4. 同步类
    对象锁
  5. 同步对象实例

类锁和实例锁不会相互阻塞,但是相同的类锁,相同的当前实例锁,相同的对象锁会相互阻塞

线程池相关

线程池说明参考:https://blog.csdn.net/ChrisLee2013/article/details/131818811

为什么使用线程池

线程池的分类

核心参数

线程池的工作流程

线程池的原理

拒绝策略

如何关闭线程池

线程池中阻塞队列的作用?为什么是先添加列队而不是先创建最大线程?

线程池中线程复用原理

线程池submit和execute的区别

谈谈多线程中的CompletionService

谈谈多线程中的ExecutorCompletionService

Java实现异步编程的方案

线程池中的线程抛出了异常,如何处理

  1. 在线程具体的处理逻辑中使用try-catch异常块捕获异常处理异常。
  2. 自定义一个ThreadFactory线程工厂,然后设置Thread线程多的Thread.setUncaughtExceptionHandler方法捕获异常
    代码实现:
/**
 * @Author chenwenlin
 * @Date 2023-07-20 23:02
 */
public class UncaughtExceptionHandler {

    public static void main(String[] args) {
        // 创建线程工厂
        ThreadFactory threadFactory = (Runnable r) -> {
            Thread thread = new Thread(r);
            thread.setUncaughtExceptionHandler((Thread thread1, Throwable e) -> {
                System.out.println("error......");
            });
            return thread;
        };

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 10, 0, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(100), threadFactory);
        threadPoolExecutor.execute(new Task());
    }
    
    
    static class Task implements Runnable {
        @Override
        public void run() {
            int i = 1 / 0;
            System.out.println(i);
        }
    }
}

CAS

什么是CAS

乐观锁
即:比较互换
在这里插入图片描述

CAS缺点

  1. CAS是基于比较再交换,如果失败循环次数过多,导致CPU的消耗比较大。
  2. 只能保证一个变量的原子操作,无法保证非原子操作结果正确。
  3. 会出现ABA问题。

ABA问题解决

原子引用

有了AtomicInterger为什么还要搞出LongAdder

AtomicInterger如果并发数很高的时候,会造成过多的没有必要的循环,会影响CPU的性能。所以JDK8增加了一个LongAdder类。

Java有哪些原子操作类

AtomicXXX和LongAdder

LongAdder底层原理

LongAdder缺点

LongAccumulator

LongAccumulator和LongAdder怎么选

AQS

重入锁

什么是重入锁

重入锁有哪些重要的方法

重入锁怎么用

重入锁怎么理解重入

重入锁最多可以重入多少次

重入锁使用需要注意什么

Synchronized与ReentrantLock的区别

Synchronized锁的是什么

读写锁

什么是读写锁

有没有比ReadWriteLock读写锁更快的锁

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值