Java并发高频面试题

1.线程池七大参数

核心线程数:线程池中的基本线程数量

最大线程数:当阻塞队列满了之后,逐一启动

最大线程的存活时间:当阻塞队列的任务执行完后,最大线长的回收时间

时间单位

阻塞队列:当核心线程满后,后面来的任务都进入阻塞队列

线程工厂:用于生产线程

任务拒绝策略:阻塞队列满后,拒绝任务,有四种策略(1)抛异常(2)丢弃任务不抛异常(3)打回任务(4)尝试与最老的线程竞争

2.线程池有以下几种状态:

  1. RUNNING:表示线程池处于运行状态,可以接收新的任务并处理。

  2. SHUTDOWN:表示线程池已经关闭,不再接收新的任务,但会处理完正在等待处理的任务。

  3. STOP:表示线程池已经关闭,不再接收新的任务,也不会处理已经在等待处理的任务,会中断正在执行的任务。

  4. TIDYING:表示线程池中所有的任务都已经执行完毕,处于整理状态,即将进入 TERMINATED 状态。

5.TERMINATED:表示线程池已经彻底关闭,不再接收任何任务。

线程池状态的转换是由线程池内部的状态变量控制的,可以通过调用线程池的 shutdown()、shutdownNow()、awaitTermination() 等方法来改变线程池的状态。

3.死锁的四个条件

互斥条件:指进程/线程对所需的资源具有排他性,即在一段时间内某资源只能被一个进程/线程占用。

请求与保持条件:指进程/线程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已经被其他进程/线程占用,此时请求进程/线程会阻塞,但不会释放已经获得的资源。

不剥夺条件:指进程/线程已经获得的资源在未使用完之前,不能被其他进程/线程强行剥夺,只能由进程/线程自己释放。

循环等待条件:指进程/线程之间形成了一种循环等待资源的关系,即进程/线程之间形成了一个循环等待的环路,每个进程/线程都在等待下一个进程/线程所占用的资源。

4.要等待多个线程执行完之后再执行下一步操作有那些方法?

方法一:用sleep方法
方法二:Thread的join()方法
方法三:等待多线程完成的CountDownLatch
方法四:同步屏障CyclicBarrier
方法五:线程池

5.线程状态,等待和阻塞状态的区别

线程状态是指一个线程在执行过程中所处的状态,Java 中线程的状态包括以下几种:

NEW:新创建的线程尚未启动。

RUNNABLE:线程正在运行或者在等待 CPU 时间片。

BLOCKED:线程被阻塞等待监视器锁,也就是在等待其他线程释放锁。

WAITING:线程处于等待状态,等待其他线程的特定操作,如等待 I/O 操作完成、等待线程执行完毕等。

TIMED_WAITING:线程处于等待状态,等待其他线程的特定操作,但是等待时间有限制,如等待 5 秒钟后继续执行。

TERMINATED:线程已经执行完毕。

等待和阻塞状态都是线程的一种状态,但是它们之间有以下区别:

阻塞状态是由于线程等待获取某个锁而被挂起,直到获取到锁之后才能继续执行。而等待状态是由于线程等待某个条件满足而被挂起,直到条件满足后才能继续执行。

在阻塞状态下,线程被挂起时会释放持有的锁,其他线程可以获取锁并继续执行。而在等待状态下,线程被挂起时不会释放持有的锁,其他线程无法获取锁并继续执行。

阻塞状态是由线程自身进入的,而等待状态则是由程序代码调用某个方法后进入的。

6.线程池如何使用,他的参数有哪些说一下

Java 中的线程池是通过 Executor 框架实现的,主要包括以下几个类和接口:

Executor:是一个接口,定义了一个 execute() 方法,用于执行一个任务。

ExecutorService:是一个接口,继承了 Executor 接口,定义了一些管理线程池的方法,如 submit()、shutdown()、awaitTermination() 等。

ThreadPoolExecutor:是一个类,实现了 ExecutorService 接口,提供了一个灵活可配置的线程池实现。

7.并发的根源是什么?
多线程共享资源时的竞争和访问顺序不确定性

8.怎么实现多线程顺序打印abc(引出Reentrantlock)

使用 ReentrantLock 和 Condition 来实现线程之间的通信。具体步骤如下:

定义一个 ReentrantLock 对象和三个 Condition 对象,用于线程之间的同步和通信。

定义三个线程 A、B、C,分别打印字母 “A”、“B”、“C”。

在每个线程中,使用 while 循环来控制打印次数,先获取锁对象,判断当前是否轮到该线程打印,如果不是则调用 await() 方法等待,如果是则打印字母并唤醒下一个线程。最后释放锁对象。

在主线程中启动三个线程,并等待它们全部执行完毕。

9…创建线程的方式

继承 Thread 类并重写 run() 方法
这是最基本的创建线程的方式,只需要继承 Thread 类并重写 run() 方法,然后调用 start() 方法启动线程即可。

实现 Runnable 接口
实现 Runnable 接口并重写 run() 方法,然后将 Runnable 对象传递给 Thread 类的构造方法创建线程。

实现 Callable 接口
实现 Callable 接口并重写 call() 方法,可以返回一个结果,然后将 Callable 对象传递给 FutureTask 类的构造方法创建线程。

使用线程池
使用线程池可以更好地管理和控制线程的数量,可以通过 Executors 类来创建线程池。

10.sleep和wait的区别
相同点:1、都可以使线程阻塞

不同点:
sleep() 是 Thread 类中的静态方法,wait() 是 Object 类中的实例方法。
sleep() 方法不会释放对象的锁(睡眠时不释放锁),wait() 方法会释放对象的锁(睡眠时,会释放互斥锁)。
sleep() 方法中有参数,要求必须定义一个过期时间,到期会主动恢复,而wait()没有参数,永久等待,直到被中断或唤醒才恢复,不会主动恢复。
wait必须与synchronized一起使用,sleep不用。
11.怎么保证多线程的安全?

使用同步代码块
使用同步代码块可以保证同一时间只有一个线程访问共享资源,从而避免多个线程同时访问导致的数据不一致问题。同步代码块的使用方式为:
synchronized (lock) {
// 访问共享资源的代码
}
其中 lock 表示锁对象,可以是任何对象。在同步代码块中,只有获取到锁对象的线程才能执行代码块中的代码,其他线程需要等待锁的释放才能继续执行。

使用 synchronized 方法
使用 synchronized 方法可以使整个方法成为同步代码块,从而避免多个线程同时访问导致的数据不一致问题。其使用方式为:
public synchronized void method() {
// 访问共享资源的代码
}
使用 synchronized 方法时,整个方法都会被加锁,只有获取到锁的线程才能执行该方法,其他线程需要等待锁的释放才能执行该方法。

使用 Lock 锁
除了使用 synchronized 关键字外,还可以使用 Lock 锁来保证多线程的安全。Lock 锁是一个接口,其实现类提供了更灵活的加锁和解锁方式。其使用方式为:
Lock lock = new ReentrantLock();
try {
lock.lock();
// 访问共享资源的代码
} finally {
lock.unlock();
}
在使用 Lock 锁时,需要在 try-finally 代码块中加锁和解锁,以避免因为抛出异常而导致锁无法释放的问题。

使用原子类
Java 中提供了一些原子类,如 AtomicInteger、AtomicLong 等,这些类可以保证对其进行操作的线程安全,从而避免多个线程同时访问导致的数据不一致问题。其使用方式为:
AtomicInteger atomicInt = new AtomicInteger();
atomicInt.incrementAndGet(); // 原子性的自增操作
在使用原子类时,可以通过其提供的方法实现对其进行原子性的操作,从而避免了多个线程同时访问导致的数据不一致问题。

12、什么是Threadlocal?实现原理?内存泄漏?
理解为线程本地变量,如果创建这样一个变量,多个线程访问这个变量就会创建一份本地拷贝,这样实际操作自己的本地内存的变量,实现线程隔离,线程安全。在数据库连接池、会话管理中使用。
每个Thread中都有个ThreadLocalMap成员变量,ThreadLocalMap中维护了一个Entry数组,每个Entry代表一个对象,key使ThreadLocal本身,value是它的泛型值。每个在ThreadLocal读写都是往自己的ThreadLocalMap里弄得,这样实现了线程隔离。
ThreadLocalMap中使用key为ThreadLocal的弱引用,value是强引用。垃圾回收时key被清理掉,value永远无法回收,ThreadLocalMap中会出现key为null的Entry,这时会产生内存泄漏,但ThreadLocalMap中的remove()可以清理掉key为null的记录。

13、什么是线程安全?
指在多个线程环境下,对共享资源的访问不会出现问题。保证线程安全有:
1、同步访问共享资源
2、线程不可变
3、线程本地存储
14、谈谈volatile的使用与原理?
volatile保证可见性和有序性,但不保证原子性。
volatile的原理是通过CPU的缓存一致性来实现的,当一个线程修改了volatile的值,他会立刻把它刷新到主内存中,其他线程访问变量时访问的是最新值,这是可见的。
15、线程有几种状态?
1、New (新键) 2、Runnable (运行)3、Blocked (阻塞) 4、Waiting (等待) 5、TimeWaiting(计时等待) 6、Terminated(终止)
16、什么是CAS?
CAS是一种并发算法,类比乐观锁,他会比较内存中的值和预期值是否相等,相等就更新内存中的值,不然失败。CAS可以保证线程安全,但会导致ABA问题。
17、什么是ABA问题?
就是初始条件是A,发现是A就会修改,但是虽然看到的是A,但是中间可能经过A变B变A的过程,不知道A到底修改没。我们用原子引用类,通过控制变量值的版本来保证CAS的原子性。
18、什么是AQS?
AQS是Java中用于实现锁和同步器的基础框架,可以把AQS比作一个门卫负责控制进入场所的人数和顺序,当一个线程想要获得某个锁或同步器时,就像一个要入场的人,首先向门卫申请,如果申请成功就可以进入场所(获取同步状态),申请失败就要排队等待。
AQS通过维护一个双向队列来实现等待线程的管理,当一个线程获取同步状态失败时,会被加入到队列,等待同步状态可用时再唤醒(排队的人等待入场)。
19、AQS组件有?
Semaphore(信号量)同时允许多个线程访问资源
CountDownLatch(倒计时器)它可以控制一个线程进行等待,一直到倒计时结束再开始执行。
CyclicBarrier(循环栅栏)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值