java八股文-多线程

目录

1. 线程基础知识

1.1 进程线程

1.2 并行,并发

1.3 创建线程的方法?

1.3.1 方法

1.3.2 runnable和callble区别

 1.3.3 run()和start()有什么区别?

1.3.4 函数式接口

1.4 线程包含的状态,状态之间的切换

1.2.1 线程包括哪些状态?

1.4.2 线程状态之间如何变化的? 

1.4.3 notify 和notifyall

1.5 新建T1T2T3线程按顺序执行?

1.6 wait和sleep方法不同?

1.7 如何停止正在运行的线程?

1.8 启动线程为啥是start(),不是run()

1.9 守护线程

1.10 线程之间通通信方式

2. 锁

2.1sychronized关键在的底层原理 (底层实现依赖Monitor)

2.1.1 对象在hotspot(堆的实现)中存在 下面的两个图,用来表明对象怎么和锁关联上的

2.1.2 锁升级

2.2 JMM (java内存模型)

2.3 CAS知道吗? (compare and swap)

2.4 volatile 理解

2.5 AQS,是否公平锁?

2.6 ReentranLock实现原理?

2.7 sychronized和lock区别?

2.8 死锁?

2.9 ConcurrentHashMap?

2.10 并发程序出现问题的原因?

3.线程池

3.1 核心线程池的参数?

3.2 线程池的执行原理

3.3 常见阻塞队列

3.4 如何确定核心线程数(和Cpu有关)

3.5 线程池的种类

3.6 为啥不建议使用Executor创建线程池

3.7线程池的状态

4.使用场景

4.1 线程池的使用(countdownlauch,future)

4.2 countdownlauch的使用场景

4.3 用户下单

4.4 异步调用

4.5 控制方法并发访问的线程数量

4.6 threadlocal

5.线上问题

5.1 多次下单


1. 线程基础知识

1.1 进程线程

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

1.2 并行,并发

现在都是多核CPU,在多核CPU下

  •  并发是同一时间应对多件事情的能力,多个线程轮流使用一个或多个CPU.
  • 并行是同一时间动手做多件事的能力,4核CPU同时执行4个线程.

1.3 创建线程的方法?

1.3.1 方法

  • 继承Thread类
  • 实现runnable接口
  • 实现Callable接口
  • 线程池创建线程(项目中使用方式)

1.3.2 runnable和callble区别

  •  runnable接口run方法没有返回值.
  • callable接口call方法有返回值,需要FutureTask获取结果.
  • callable接口的call()方法允许抛出异常,而runnable接口run()方法的异常只能内部消化,不能继续上抛

 1.3.3 run()和start()有什么区别?

  • start():用来启动线程,通过该线程调用用run方法执行run方法中所定义的逻辑代码,start方法只能被调用一次
  • run() :封装了要被线程执行的代码,可以调用多次.
  • run():方法只是一个普通方法,调用它并不会创建新线程,只是在当前线程中执行。
  • start():方法用于启动一个新线程,并在新线程中执行run()方法中的逻辑。

1.3.4 函数式接口

函数式接口 : 类中只有一个抽象方法 ,可以存在默认方法 即default修饰的方法 (不是只能由抽象方法存在了)

比如 线程 Thread thread = ()->{system.out.print("1")} .他就知道实现了那个方法,多个抽象方法就不行了

1.4 线程包含的状态,状态之间的切换

1.2.1 线程包括哪些状态?

  • NEW(新建状态):当线程对象被创建但还没有调用start()方法时,线程处于新建状态。

  • RUNNABLE(可运行/运行状态):线程调用start()方法后,线程进入可运行状态。在可运行状态下,线程可能正在执行,也可能等待操作系统分配执行时间。

  • BLOCKED(阻塞状态):线程被阻塞等待一个监视器锁,例如在synchronized块中调用了wait()方法,线程将进入阻塞状态。

  • WAITING(等待状态):线程无限期地等待另一个线程执行特定操作,例如调用Object.wait()方法或Thread.join()方法,使线程进入等待状态。

  • TIMED_WAITING(计时等待状态):线程等待另一个线程执行特定操作,但等待一段时间后会自动返回,例如调用Thread.sleep()方法或Object.wait(timeout)方法。

  • TERMINATED(终止状态):线程执行完run()方法中的代码或者因为异常退出后,线程进入终止状态。

1.4.2 线程状态之间如何变化的? 

  • 创建线程对象是新建状态
  • 调用了start方法装变为可执行状态
  • 线程获取到CPU执行权,执行结束是终止状态.
  • 在可执行状态过程中,如果没有获取CPU的执行权,可能会切换其他状态

               1. 如果没有获取锁(synchronized或lock)进入阻塞状态,获取后切换为可执行

               2.如果线程调用了wait()方法进入等待状态,其他线程调用notify()唤醒后可切换为可执行状态

               3. 如果调用了sleep()方法,进入计时等待状态,到时间后可以切换为可执行

1.4.3 notify 和notifyall

  1. notify()

    • notify()方法用于唤醒在对象监视器上等待的单个线程。如果有多个线程在对象监视器上等待,系统会随机选择其中一个线程唤醒,使其从等待状态变为就绪状态。
    • 被唤醒的线程会尝试重新获取对象的锁,一旦成功获取锁,它就可以继续执行。
  2. notifyAll()

    • notifyAll()方法用于唤醒在对象监视器上等待的所有线程。所有等待在对象监视器上的线程都会被唤醒,从等待状态变为就绪状态。
    • 然后,这些线程会竞争对象的锁,只有一个线程能够获取到锁并继续执行,其他线程会继续等待。

1.5 新建T1T2T3线程按顺序执行?

join实现就好

public class ThreadExecutionOrder {

    public static void main(String[] args) {
        Thread t1 = new Thread(new MyThread(), "T1");
        Thread t2 = new Thread(new MyThread(), "T2");
        Thread t3 = new Thread(new MyThread(), "T3");

        try {
            // 启动线程T1
            t1.start();
            t1.join(); // 等待T1执行完毕

            // 启动线程T2
            t2.start();
            t2.join(); // 等待T2执行完毕

            // 启动线程T3
            t3.start();
            t3.join(); // 等待T3执行完毕
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    static class MyThread implements Runnable {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " is running");
        }
    }
}

1.6 wait和sleep方法不同?

1.7 如何停止正在运行的线程?

三种方式:

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

               . 打断阻塞的线程(sleep,eait,join)的线程,线程抛出interrutedException异常

               . 打断正常线程,可以根据打断状态来标记是否退出线程.

  • 打断正常线程会修改currentthread.isinterrcepted的状态为true

1.8 启动线程为啥是start(),不是run()

run 方法直接调用就是在主线程直接执了 ,

start() 并不是单单一个方法, 会先创建一个线程 ,来执行run

1.9 守护线程

1.10 线程之间通通信方式

左侧第一种 ,间接通信 ,锁上了修改后,他线程能感知到

2. 锁

2.1sychronized关键在的底层原理 (底层实现依赖Monitor)

2.1.1 对象在hotspot(堆的实现)中存在 下面的两个图,用来表明对象怎么和锁关联上的

2.1.2 锁升级

Minotor重量级开销大,没有竞争的时候使用轻量锁,一旦有竞争就会升级为重量级的

2.2 JMM (java内存模型)

线程私有的内存+共享内存

2.3 CAS知道吗? (compare and swap)

体现了乐观锁的实现原理,不加锁,每次会用旧值和之前的比较,不一样重新获取执行直到相等,或者设置阈值,多少次不在比较

2.4 volatile 理解

因为jit优化了导致指令重排,直接禁止的话影响其他线程使用

2.5 AQS,是否公平锁?

2.6 ReentranLock实现原理?

多个线程抢资源保证原子性cas

  • 公平非公平锁是体现在线程加锁的阶段 : 线程肯定有等待队列,非公平的不会来了就去队列排队,而是尝试获取锁,获取不到
  • 就回去队列等待了. 公平锁来了就去排队

2.7 sychronized和lock区别?

2.8 死锁?

2.9 ConcurrentHashMap?

2.10 并发程序出现问题的原因?

3.线程池

3.1 核心线程池的参数?

3.2 线程池的执行原理

3.3 常见阻塞队列

使用的多的是linkedBlockingQueue,说我们项目是的这个,默认无界但是我们会给个界限

3.4 如何确定核心线程数(和Cpu有关)

3.5 线程池的种类

1.适用任务量已知,相对耗时的任务

2. 使用于顺序执行的线程

3. 适合任务数比较密集,每个任务执行短的情况

4.适合周期性的

3.6 为啥不建议使用Executor创建线程池

这个就是创建上面的线程池,因为默认的最大线程数是interger.maxvalue,可能会耗尽资源

3.7线程池的状态

4.使用场景

4.1 线程池的使用(countdownlauch,future)

4.2 countdownlauch的使用场景

countdownlauch用来等待子线程执行完成后在执行主线程,执行任务还是线程池,导入数据到es

4.3 用户下单

用户下单完后,查询信息,商品,物流,订单在三个微服务,如何完成?

顺序执行慢,所以多线程,用countdownlauch,等待都完成在返回就好了,但是需要 获取线程返回值,需要用callable

4.4 异步调用

@enableansy 启动类添加开启异步调用

@Ansy("")

4.5 控制方法并发访问的线程数量

4.6 threadlocal

threadlocal ,你只要实现了thread或者实现runable 都有threadlocalmap threadlocal的

使用场景 例如这个线程调用三个类的多个方法,只需要把用到的对象,放到threadlocal这样就不用再每个方法声明了,需要的时候只要threadlocalget就好了

不能set多次,会覆盖 ,

5.线上问题

5.1 多次下单

描述: 可能网络问题,导致多次点击,生成多个订单(占用内存,体验差)

原因:网络延迟,或者系统卡顿

解决:前端按钮置灰,后端使用redis的setnx,然后缓存token+url+商品id,设置几秒过期时间,(一般不会几秒内重复购买同一商品)

### Java多线程常见面试题解析 #### 1. 如何确保多个线程按特定顺序执行? 为了确保三个线程 T1, T2 和 T3 按照指定顺序执行,可以使用 `join()` 方法或者手动锁机制。`join()` 方法可以让当前线程等待另一个线程完成后再继续执行[^1]。 以下是通过 `join()` 实现的代码示例: ```java public class OrderedThreads { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> System.out.println("T1 is running")); Thread t2 = new Thread(() -> { try { t1.join(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("T2 is running"); }); Thread t3 = new Thread(() -> { try { t2.join(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("T3 is running"); }); t1.start(); t2.start(); t3.start(); // Ensure the main thread waits for all threads to finish. t1.join(); t2.join(); t3.join(); System.out.println("Main thread finished."); } } ``` 另一种方式是使用显式的同步工具如 `CountDownLatch` 或者 `ReentrantLock` 来控制线程间的执行次序[^4]。 --- #### 2. 线程池中的核心参数有哪些?其作用是什么? 线程池的核心参数包括以下几个方面: - **corePoolSize**: 表示线程池中保持存活的最小线程数。 - **maximumPoolSize**: 表示线程池允许的最大线程数。 - **keepAliveTime**: 如果当前线程数超过了 corePoolSize,则多余的空闲线程会在 keepAliveTime 时间后被销毁。 - **workQueue**: 存放待处理任务的阻塞队列。 当提交的任务数量超过最大线程数且队列已满时,线程池会拒绝新任务并抛出 `RejectedExecutionException` 异常[^2]。 --- #### 3. 启动自定义线程的方式有哪些? 启动一个自定义线程可以通过继承 `Thread` 类或实现 `Runnable` 接口两种主要方式。推荐使用后者,因为它不会占用额外的类层次结构空间[^3]。 下面是一个基于 `Runnable` 的例子: ```java class MyTask implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName() + " is executing..."); } } public class TaskRunner { public static void main(String[] args) { MyTask task = new MyTask(); Thread thread = new Thread(task); thread.start(); } } ``` --- #### 4. 如何在线程间安全地共享数据? 在 Java 中,可以在两个线程之间共享数据的方法有很多种,比如使用 `volatile` 关键字、`synchronized` 块/方法以及高级并发工具(如 `AtomicInteger`, `ConcurrentHashMap`)。对于更复杂的场景,还可以考虑使用 `BlockingQueue` 或者显示锁 `ReentrantLock`。 以下是一个简单的 `synchronized` 示例: ```java class SharedResource { private int value; public synchronized void increment() { this.value++; System.out.println(Thread.currentThread().getName() + ": Value incremented to " + value); } public synchronized int getValue() { return value; } } public class DataSharingExample { public static void main(String[] args) { final SharedResource resource = new SharedResource(); Thread producer = new Thread(() -> { for(int i=0; i<5; ++i){ resource.increment(); } }, "Producer"); Thread consumer = new Thread(() -> { while(resource.getValue() < 5){ try{ Thread.sleep(100); }catch(Exception ex){} } System.out.println("Consumer: Final value reached "+resource.getValue()); }, "Consumer"); producer.start(); consumer.start(); } } ``` --- #### 5. 死锁是如何产生的?如何预防死锁? 死锁通常发生在多个线程互相持有对方所需的资源而无法继续运行的情况下。要防止死锁的发生,可以采取以下措施之一:按照固定的顺序获取锁;避免嵌套锁定操作;设置超时时间等。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

半斤拿铁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值