多线程面试题2
1.如何预防死锁?
2.描述一下线程安全活跃态问题,竞态条件?
两个或多个线程同时对一共享数据进行修改,从而影响程序运行的正确性时,这种就被称为竞态条件(race condition)
3.描述一下进程与线程区别?
4.程序开多少线程合适?
5.描述一下notify和notifyAll区别?
首先最好说一下池锁和等待池的概念
然后再来说notify和notifyAll的区别
6.多线程之间是如何通信的?
多进程之间通信:信号量、信号、套接字、有名管道、无名管道、共享内存、mq
由于多线程之间共享了进程的地址空间,所以,原生就支持数据共享,当一个线程修改了进程的变量,由于共享空间,另外一个线程自然能看到,所以原生支持通信。由于线程之间并发必然会引起互斥操作,这时就需要同步机制:
volatile、sync、ReentrantLock、ReadWriteReenTrantLock
7.重入锁的实现原理
8.Volatile原理
至于是怎么发现数据是否失效呢?
嗅探的缺点
禁止指令重排序:
volatile通过内存屏障来禁止指令重排序
JMM内存屏障的策略
在每个volatile写操作的前面插入一个StoreStore屏障
在每个volatile写操作的后面插入一个StoreLoad屏障
在每个volatile读操作的后面插入一个LoadLoad屏障
在每个volatile读操作的后面插入一个LoadStore屏障
9.Condition和wait/notify的比较
10.JUC 下常用的同步工具类
CountDownLatch
它是一个同步辅助器,允许一个或多个线程一直等待,直到一组在其他线程执行的操作全部完成。
CyclicBarrier
一组线程会互相等待,直到所有线程都到达一个同步点。这个就非常有意思了,就像一群人被困到了一个栅栏前面,只有等最后一个人到达之后,他们才可以合力把栅栏(屏障)突破。
Semaphore
11.ThreadLocal
作用
应用场景
底层原理
12.Executor提供了几种线程池
13.线程池的参数
14.线程池拒绝策略
15.执行execute()方法和submit()方法的区别是什么呢?
16.了解过线程池的工作原理吗?
17.Java中wait和sleep的区别
18.实现一下DCL
public class SingleInstance {
private SingleInstance() {}
private static volatile SingleInstance INSTANCE;
public static SingleInstance getInstance() {
if (INSTANCE == null) {
synchronized (SingleInstance.class) {
if (INSTANCE == null) {
INSTANCE = new SingleInstance();
}
}
}
return INSTANCE;
}
}
DDL单例为什么要加volatile
19.实现一个阻塞队列(用Condition写生产者与消费者)
package com.milla.study.netbase.expert.concurrent.lock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Package: com.milla.study.netbase.expert.concurrent.lock
* @Description: <阻塞队列>
* @Author: MILLA
* @CreateDate: 2020/6/1 18:14
* @UpdateUser: MILLA
* @UpdateDate: 2020/6/1 18:14
* @UpdateRemark: <>
* @Version: 1.0
*/
public class ListBlockLockQueue {
/**
* 1.定义个队列
* 2.采用可重入锁及信号量进行控制
* 3.数据存储时,如果数据已经满了,则存储线程进行阻塞,唤醒获取数据线程
* 4.数据获取时,如果当前数据已经消费完,则获取线程进行阻塞,获取存储数据线程
* 5.数据的存储按照从0到队列的长度存入,如果存满则存储索引置0
* 6.数据的获取按照从0到队列的长度获取,如果存满则存储索引置0
*/
//定义队列容量
private Integer size = 5;
//采用集合定义一个队列
private final Object[] data = new Object[size];
//定义可重复锁
ReentrantLock lock = new ReentrantLock();
//获取为空信号量
Condition take = lock.newCondition();
//获取为满信号量
Condition put = lock.newCondition();
//获取数据索引值
int takeIndex;
//加入数据索引值
int putIndex;
//当前有多少个数据可取
int count;
/**
* 拉取一个数据
*
* @return
*/
public Object take() throws InterruptedException {
lock.lock();
try {
//没有数据可取-取数据线程阻塞
while (count == 0) {
take.await();//阻塞
}
//获取先存如数据
Object o = data[takeIndex];
//数据获取索引达到队列长度(说明当前数据取完了)-将获取索引置为0
if (++takeIndex == data.length) {
takeIndex = 0;
}
--count;
System.out.println(Thread.currentThread().getName() + ",takeIndex: " + takeIndex + " count: " + count);
put.signal();
return o;
} finally {
lock.unlock();
}
}
/**
* 存入数据
*
* @param obj
*/
public void put(Object obj) throws InterruptedException {
//先上锁
lock.lock();
try {
//如果已经满了-阻塞
while (count == data.length) {
//就不能继续添加
put.await();
}
//添加元素
data[putIndex] = obj;
//添加数据索引达到队列长度-将索引值重置为0
if (++putIndex == data.length) {
putIndex = 0;
}
++count;
System.out.println(Thread.currentThread().getName() + ",takeIndex: " + takeIndex + " count: " + count);
take.signal();
} finally {
//最后释放锁
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
ListBlockLockQueue queue = new ListBlockLockQueue();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 3; i++) {
try {
queue.put("" + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "一");
Thread t2 = new Thread(() -> {
while (true) {
try {
System.out.println(Thread.currentThread().getName() + ",获取数据: " + queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "二");
t2.start();
t1.start();
Thread.sleep(1000);
queue.put("123456");
Thread.sleep(1000);
queue.put("00000");
Thread.sleep(1000);
queue.put("555555");
}
}
20.实现多个线程顺序打印abc?
public class JoinDemo{
//三个线程顺序执行
public static void main(String[] args) {
Work t1=new Work(null,"A");
Work t2=new Work(t1,"B");
Work t3 =new Work(t2,"C");
t1.start();
t2.start();
t3.start();
}
static class Work extends Thread{
private Thread Thread;
private String threadName;
public Work(Thread Thread,String threadName) {
this.Thread = Thread;
this.threadName=threadName;
}
@Override
public void run() {
if (Thread != null) {
try {
Thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(threadName);
}
}
}