java面试题(高级一点的)

1 OOM的认识

java.lang.StackOverflowError
java.lang.OutOfMemoryError: Java heap space
java.lang.OutOfMemoryError: GC overhead limit exceeded
java.lang.OutOfMemoryError: Direct buffer memory
java.lang.OutOfMemoryError: Metaspace
java.lang.OutOfMemoryError: unable to create new native thread
java.lang.OutOfMemoryError: Requested array size exceeds VM limit

2 JUC多线程及高并发

java.util.concurrent
volatile是什么?
java虚拟机提供的轻量级的同步机制

JVM(java虚拟机):可见性,原子性(i++在多线程下是非线程安全的)

volatile三特性

1.保证可见性(一个线程改变主内存中的共享变量副本,再写回主内存,及时通知其他线程共享变量改变)
2.不保证原子性(不可分割,完整性:某个线程正在做某个具体业务时,中间不可以被加塞或者分隔。需要整体完整 要么成功 要么同时失败)
解决原子性:加sync,使用juc下的AtomicInteger
3.禁止指令重排
在这里插入图片描述
在这里插入图片描述

callable接口

在这里插入图片描述
在这里插入图片描述
1.get():建议放在最后(两个线程;main主线程,AAfutureTask)
2.如果没有计算完成就要去强求,会导致堵塞,直到计算完成

线程池使用

实现多线程的方法:
1.继承Thread类
2.实现Runnable接口,重写run方法,没有返回值,不抛异常
3.实现Callable接口,重写call方法,有返回值,会抛异常
4.线程池
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
ThreadPoolExcutor
在这里插入图片描述
在这里插入图片描述

线程池底层工作原理

在这里插入图片描述
在这里插入图片描述

3 单例模式在多线程可能存在安全问题

单例模式
在这里插入图片描述
多线程下
在这里插入图片描述
在getInstance方法前面加synchronized,尽量不要用哦,把整个都加锁了。

DCL(Double Check Lock双端检索机制)
在这里插入图片描述
DCL不一定线程安全,存在指令重排,加入volatile禁止指令重排。
原因在于某一个线程执行到第一次检测,读取到的instance不为null时,instance的引用对象可能没有完成初始化。instance = new SingletonDemo(); 可以分为以下3步完成(伪代码)
memory = allocate(); //1. 分配对象内存空间
instance(memory); //2.初始化对 象
instance = memory; //3. 设置instance指向刚分配的内存地址,此时instance! =null
步骤2和步骤3不存在数据依赖关系,而且无论重排前还是重排后程序的执行结果在单线程中并没有改变,因此这种重排优化是允许的。
memory = allocate(); //1.分配对象内存空间
instance = memory; //3.设置instance指向刚分配的内存地址,此时instance! =null,但是对象还没有初始化完成!
instance(memory); //2.初始化对象
但是指令重排只会保证串行语义的执行的一致性(单线程),但并不会关心多线程间的语义一致性。
所以当一条线程访问instance不为null 时,由于instance实例未必已初始化完成,也就造成了线程安全问题。
在这里插入图片描述

4 CAS

CAS:比较并交换

5 阻塞队列

在这里插入图片描述
在这里插入图片描述
blockingQueue阻塞队列
在这里插入图片描述

5.1 ArrayBlockingQueue

核心方法:
在这里插入图片描述

BlockingQueue<String> blocking = new ArrayBlockingQueue<>(3);//指定容量大小
//element检查,返回队首元素

超时退出:当阻塞队列满时,队列会阻塞生产者线程一段时间,超过后限时后生产者线程会退出。
在这里插入图片描述

5.2 SynchronousQueue

在这里插入图片描述
示例:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6 生产者消费者

在这里插入图片描述

传统版

一个初始值为 0 的变量,两个线程对其交替操作,一个加1 一个减1,来5轮

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class ShareData {  //资源类
    private int number = 0;
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    public void increment() throws Exception {
        lock.lock(); // 相当于同步代码块 加锁
        try{
            // 1 判断
            while(number != 0 ) { // 必须用while,用if就错误
                // 等待,不能生产
                condition.await();
            }
            // 2 干活
            number++;
            System.out.println(Thread.currentThread().getName()+"\t"+number);
            // 3 通知唤醒
            condition.signal();
        }catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void decrement() throws Exception {
        lock.lock(); // 相当于同步代码块 加锁
        try{
            // 1 判断
            while(number == 0 ) {
                // 等待,不能生产
                condition.await();
            }
            // 2 干活
            number--;System.out.println(Thread.currentThread().getName()+"\t"+number);
            // 3 通知唤醒
            condition.signal();
        }catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
/**
 * 1   线程   操作(方法)   资源类
 * 2   判断   干活      通知
 * 3   防止虚假唤醒机制
 */
public class TestProdConsumer_TraditionDemo {
    public static void main(String[] args) {
        ShareData shareData = new ShareData();
        // 生产
        new Thread(()->{
            for (int i = 0; i <= 5; i++) {
                try {
                    shareData.increment();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"AAA").start();
        // 消费
        new Thread(()->{
            for (int i = 0; i <= 5; i++) {
                try {
                    shareData.decrement();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"BBB").start();
    }
}

在这里插入图片描述

7 synchronized和lock区别

  1. 原始构成
    synchronized是关键字属于JVM层面,不会产生死锁。
    monitorenter(底层是通过monitor对象来完成,其实wait/notify 等方法也依赖monitor对象只有在同步块或方法中才能调wait/notify等方法)
    monitorexit
    Lock是具体类(java.util.concurrent.locks.Lock)是api层面的锁
  2. 使用方法
    synchronized不需要用户去手动释放锁,当synchronized 代码执行完后系统会自动让线程释放对锁的占用
    ReentrantLock则需要用户去手动释放锁。若没有主动释放锁,就有可能导致出现死锁现象。需要lock()和unlock()方法配合try/finally语句块来完成。
  3. 等待是否可中断
    synchronized不可中断,除非抛出异常或者正常运行完成
    ReentrantLock可中断,1.设置超时方法tryLock(long timeout, TimeUnit unit) 2.LockInterruptibly()放代码块中,调用interrupt() 方法可中断
  4. 加锁是否公平
    synchronized 非公平锁
    ReentrantLock两者都可以,默认非公平锁,构造方法可以传入boolean值,true为公平锁,false为非公平锁
  5. 锁绑定多个条件condition
    synchronized没有,要么随机唤醒一个线程,要么唤醒全部线程。
    Reentrantlock用来实现分组唤醒需要唤醒的线程们,可以精确唤醒。
    在这里插入图片描述
    在这里插入图片描述

以上资料来源尚硅谷面试视频。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值