java 面试题

数据库方面

数据库隔离级别mysql默认哪几个

隔离级别脏读不可重复读幻读
读未提交可能可能可能
读已提交不可能可能可能
可重复提交不可能不可能可能
可串行化不可能不可能不可能

数据库调优之索引

覆盖索引可以减少回表次数,可以在 explain 看执行计划时 extra 字段中有 using index condition 我们可以看到它用了没。其实还可以进一次优化,如果存取介质是机械硬盘——即害怕随机读写,有一个磁盘寻址开销,可以开开mrr,muti range read 他可以把在回表前把id读到一个 buffer里进行排序,把一个随机操作变成一个顺序操作。

可以用覆盖原则与最左原则减少一些索引的维护。

开mrr的代码

## 查询sql的优化参数
select @@optimizer_switch;

set @@optimizer_switch='mrr_cost_based=off,mrr=on';

如果是写多读少的服务,并且这个服务唯一性要求没有这么高,或者我们的业务代码可以保证唯一性,我们可以使用普通索引,因为普通索引可以用到 changeBuffer的,changeBuffer可以将一些写操作缓存下来,在我们读的时候进行merge==(将 change buffer 中的操作应用到原数据页,得到最新结果的过程称为 merge操作)==,提高写入速度,以及提高内存的利用率。

普通索引与唯一索引应该如何选择

当自身的sql没有问题后

当sql排查无误后可以看看是否是索引统计信息有问题,如果索引统计信息有问题的话我们可以用

Analyze table统计索引信息,因为索引信息并不是一个准确值,它是一个随机采样的过程

explain不可靠

explain选择出来的索引并一定是最优的,它可能会选错,因为我们在索引的时候会进行一些涉及到回表的操作,还有一些排序操作,导致其走错

索引没有建好导致走的很差该怎么办

用覆盖索引加最左原则,考虑能不能将这个选错的索引给删除。

索引失效的几个点

模型数空运最快

模糊查询%开头会造成索引失效 解决方法将查询的列改成索引相应的列

使用查询时如果有任何字段没有索引的情况,都会回表如何不让索引覆盖,将被查询的字段,建立到联合索引即将没有索引的字段新建一个索引。

型代表数据类型,数据类型错误了也会造成索引失效
数表函数,对索引的字段使用内部函数,索引也会失效,
空表null值索引不存储空值,如果索引可以存储空值数据库不会按照索引来计算
运,对索引列进行±*/运算会导致索引失效
最表左原则,在复合索引中索引列的顺序非常重要,如果不是按照索引列最左列开始查找则无法使用索引
快表示数据库认为全表扫描更快数据库就不会使用索引

java方面

StringBuffer与StringBuilder区别

效率上StringBuilder更快,但StringBuffer是线程安全的,Stringbuilder非线程安全

String.intern()使用场景

当通过语句str.intern()调用intern()方法后,JVM 就会在当前类的常量池中查找是否存在与str等值的String,若存在,则直接返回常量池中相应Strnig的引用;若不存在,则会在常量池中创建一个等值的String,然后返回这个String在常量池中的引用。节约内存

公平与非公平锁如何实现

reentrantlock()非公平

reentrantlock(true) 公平锁

线程安全的list有哪些

synchronizedlist

copyOnwriterArraylist

date与LocalDateTime区别

不推荐使用date不格式化的日期可读性差用simpleDateFormate线程不安全

LocalDateTime线程安全源码中实现了serializable

几种方法实现一个线程

继承 thread 实现 Runable接口 实现callable接口

io流一般分为几种

输入流与输出流,数据类型分为字节和字符流区别是一个按8位输入输出,一个16位输出输出

hashmap与hashtable有什么区别

hashmap key可为null

hashtable key不可为null

hashmap线程不安全

hashtable线程安全但效率不高

推荐使用 currenthashmap里面使用了volatitle

对volatile的理解?

答:volatile是Java虚拟机提供的轻量级的同步机制,它有3个特性:
1)保证可见性
2)不保证原子性
3)禁止指令重排

有通知操作,你就可以理解为就具有可见性。

原子性:要么都成功,要么就失败。

cas

ConcurrentHashMap内部大量采用CAS操作,这里我简要介绍下CAS。

CAS是compare and swap的缩写,即我们所说的比较交换。cas是一种基于锁的操作,而且是乐观锁。在java中锁分为乐观锁和悲观锁。悲观锁是将资源锁住,等一个之前获得锁的线程释放锁之后,下一个线程才可以访问。而乐观锁采取了一种宽泛的态度,通过某种方式不加锁来处理资源,比如通过给记录加version来获取数据,性能较悲观锁有很大的提高。

CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存地址里面的值和A的值是一样的,那么就将内存里面的值更新成B。CAS是通过无限循环来获取数据的,如果在第一轮循环中,a线程获取地址里面的值被b线程修改了,那么a线程需要自旋,到下次循环才有可能机会执行。

三、CAS存在的问题

CAS虽然很高效的解决原子操作,但是CAS仍然存在三大问题。ABA问题,循环时间长开销大和只能保证一个共享变量的原子操作

  1. ABA问题。因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A-2B-3A。

什么是死锁(deadlock)?

两个线程或两个以上线程都在等待对方执行完毕才能继续往下执行的时候就发生了死锁。结果就是这些线程都陷入了无限的等待中。

如何确保N个线程可以访问N个资源同时又不导致死锁?

使用多线程的时候,一种非常简单的避免死锁的方式就是:指定获取锁的顺序,并强制线程按照指定的顺序获取锁。因此,如果所有的线程都是以同样的顺序加锁和释放锁,就不会出现死锁了

多线程产生死锁的四个必要条件:

互斥条件:一个资源每次只能被一个进程使用。
保持和请求条件:一个进程因请求资源而阻塞时,对已获得资源保持不放。
不可剥夺:进程已获得资源,在未使用完成前,不能被剥夺。
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
只要破坏其中任意一个条件,就可以避免死锁,其中最简单的就是破环循环等待条件。按同一顺序访问对象,加载锁,释放锁

mybatis xml映射文件与mybatis内部数据结构之间映射关系

所有的xml封装到 All-in-on 重量级对象Configration内ParameterMap被解析成ParamterMap子元素被解析成ParameterMapping,resultMap被解析成ResultMap子元素被解析成ResultMapping 每一个select update insert delete被解析成 mapperStatement sql被解析成BoundSQl

mybatis如何分页,分页插件的原理是什么

mybatis用RowBounds分布针对ResultSet执行内存分布并非物理分页,可在sql中直接进行物理分页。

分布插件是使用Mybatis插件接口自定义插件,在插件拦截方法中拦截未执行的sql重写sql语句,根据dialect方言添加对应的物理分布语句与分页参数。

在监视器(monitor)如何做到线程同步

监视器与锁一起使用,监视一块同步代码块,确保一次只有一个线程执行同步代码块。每个监听器与一个对象引用相关联。线程在获取锁之间不允许执行同步代码块。方法或者代码块被synchronized修饰后就被放入监视器监视范围。

Synchronized与Lock的区别

Synchronized是内置Java关键字,Lock是一个Java类
Synchronized无法判断获取锁的状态,Lock可以判断是否获取到了锁
Synchronized自动释放锁,Lock手动释放锁,如果不释放,就会死锁。
Synchronized 适合锁少量的代码同步问题,Lock适合锁大量的同步代码

sleep()、wait()、join()、yield()

  • wait():属于 Object 的方法,当前线程调用对象的 wait()方法,会释放锁,进入线程的等待队列,需要依靠notify()或者notifyAll()唤醒,或者wait(timeout)时间自动唤醒。
  • sleep():属于线程 Thread 的方法,让线程暂缓执行,等待预计时间之后再恢复,交出CPU使用权,不会释放锁,抱着锁睡觉!进入超时等待状态TIME_WAITGING,睡眠结束变为就绪Runnable
  • yield():属于线程 Thread 的方法,暂停当前线程的对象,去执行其他线程,交出CPU使用权,不会释放锁,和sleep()类似,让相同优先级的线程轮流执行,但是不保证一定轮流,
    注意:不会让线程进入阻塞状态 BLOCKED,直接变为就绪 Runnable,只需要重新获得CPU使用权。
  • join():属于线程 Thread 的方法,在主线程上运行调用该方法,会让主线程休眠,不会释放锁,让调用join()方法的线程先执行完毕,再执行其他线程。类似让救护车警车优先通过!!

Thread 调用 start() 方法和调用 run() 方法的区别

run():普通的方法调用run()函数,在主线程中执行,不会新建一个线程来执行

start():新启动一个线程,这时此线程处于就绪(可运行)状态,并没有真正运行,一旦得到 CPU 时间片,就调用 run() 方法执行线程任务。

并发、并行、串行

并行:时间重叠,两个任务在同一时刻互不干扰执行
并发:允许两个任务互相干扰,两者交替执行,同一时间只能有一个任务执行
串行:多个任务挨个执行,时间不可能发生重叠

请你说一下线程的几个状态(生命周期)?

线程通常有五种状态,新建、就绪、运行、阻塞和死亡状态:

新建状态(New):线程刚被创建,但尚未启动。如:Thread t = new Thread();
就绪状态(Runnable):当调用线程对象的start()方法后,线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行。
运行状态(Running):当 CPU 开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。
阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:

  • 等待阻塞 :运行的线程执行wait()方法,该线程会释放占用的所有资源,JVM会把该线程放入“等待池”中。进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify()或notifyAll()方法才能被唤 醒,wait()是 Object 类的方法。
  • 同步阻塞:当线程处于运行状态时,试图获得某个对象的同步锁时,如果该对象的同步锁已经被其他线程占用,Java虚拟机就会把这个线程放到这个对象的锁池中。
  • 其他阻塞状态:当前线程执行了sleep()方法,或者调用了其他线程的join()方法,或者发出了 I/O 请求时,就会进入这个状态。线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者 I/O处理完毕时,线程重新转入就绪状态。

死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

Spring Bean 的注入方式?

参考文章:Spring定义bean的注入方式

  • 构造器注入
  • setter 方法注入
  • 接口注入
  • 静态工厂的方法注入
  • 实例工厂的方法注入

静态内部类和匿名内部类之间的区别?

内部类:

  • 成员内部类可访问外部类所有的方法和成员变量。

  • 不能有静态的方法和成员变量。

静态内部类:

  • 只能访问外部类的静态成员变量与静态方法。
  • 静态内部类的非静态成员可访问外部类的静态变量,而不可访问外部类的非静态变量
    匿名内部类:
  • 没有类名,没有 class 关键字也没有 extends 和 implements 等关键字修饰。
  • 类的定义和对象的实例化同时进行。

请问Java中可以有哪些方法来保证线程安全?

加锁:比如synchronize/ReentrantLock。
使用 volatile 声明变量,轻量级同步,不能保证原子性(需要解释)。
使用线程安全类,例如原子类 AtomicXXX等。其底层就是volatile和CAS 共同作用的结果:

使用线程安全集合容器,例如:CopyOnWriteArrayList/ConcurrentHashMap等。

  1. CopyOnWriteArrayList实现了List接口,因此它是一个队列。

  2. CopyOnWriteArrayList包含了成员lock。每一个CopyOnWriteArrayList都和一个监视器锁lock绑定,通过lock,实现了对CopyOnWriteArrayList的互斥访问。

  3. CopyOnWriteArrayList包含了成员array数组,这说明CopyOnWriteArrayList本质上通过数组实现的。

  4. CopyOnWriteArrayList的“动态数组”机制 – 它内部有个“volatile数组”(array)来保持数据。在“添加/修改/删除”数据时,都会新建一个数组,并将更新后的数据拷贝到新建的数组中,最后再将该数组赋值给“volatile数组”。这就是它叫做CopyOnWriteArrayList的原因!CopyOnWriteArrayList就是通过这种方式实现的动态数组;不过正由于它在“添加/修改/删除”数据时,都会新建数组,所以涉及到修改数据的操作,CopyOnWriteArrayList效率很低;但是单单只是进行遍历查找的话,效率比较高。

  5. CopyOnWriteArrayList的“线程安全”机制 – 是通过volatile和ReentrantLock来实现的。 final transient ReentrantLock lock = new ReentrantLock(); private transient volatile Object[] array;

  6. CopyOnWriteArrayList是通过“volatile数组”来保存数据的。一个线程读取volatile数组时,总能看到其它线程对该volatile变量最后的写入;就这样,通过volatile提供了“读取到的数据总是最新的”这个机制的 保证。

  7. CopyOnWriteArrayList通过 ReentrantLock 实现

  8. public E set(int var1, E var2) {
        ReentrantLock var3 = this.lock;
        var3.lock();//上锁
    
        Object var11;
        try {
            Object[] var4 = this.getArray();
            Object var5 = this.get(var4, var1);
            if (var5 != var2) {
                int var6 = var4.length;
                Object[] var7 = Arrays.copyOf(var4, var6);
                var7[var1] = var2;
                this.setArray(var7);
            } else {
                this.setArray(var4);
            }
    
            var11 = var5;
        } finally {
            var3.unlock();//修改完了释放
        }
    
        return var11;
    }
    

ThreadLocal本地私有变量/信号量 Semaphore等

ReentrantLock

【1】什么是可重入锁? 它用来解决什么问题? 【2】ReentrantLock 的核心是 AQS,那么它怎么来实现的,继承吗? 说说其类内部结构关系。 【3】ReentrantLock 是如何实现公平锁的? 【4】ReentrantLock 是如何实现非公平锁的? 【5】ReentrantLock 默认实现的是公平还是非公平锁?非公平 【6】使用ReentrantLock 实现公平和非公平锁的示例?new 的时候给个true为公平不给默认非公平 【7】ReentrantLock 和 Synchronized的对比?

  1. ReentrantLock 重入锁 一个持有锁的线程,在释放锁之前。此线程如果再次访问了该同步锁的其他的方法,这个线程不需要再次竞争锁,只需要记录重入次数。重入锁的设计目的是为了解决死锁的问题

2. AQS

核心是一个一个以Node为节点实现的链表的队列(CHL队列),还有一个STATE标志,并且通过CAS来改变它的值。

其实现方式为:
独占锁需要实现的是 tryAcquire(int)、tryRelease(int)
共享锁需要实现的是 tryAcquireShared(int)、tryReleaseShared(int)

在重写这些方法时,如果想要使用state同步变量,必须使用AQS内部提供的以下方法来控制:

/** 返回同步状态的当前值(此操作具有volatile变量的读语义) **/
protected final int getState() {  //方法被final修饰,不允许被重写
        return state;
}
 /** 设置同步状态的值(此操作具有volatile变量的写语义) **/
protected final void setState(int newState) { //方法被final修饰,不允许被重写
        state = newState;
}
/**
 * 原子地(CAS操作)将同步状态值设置为给定值update如果当前同步状态的值等于expect(此操作具有volatile变量的读写语义)
 * @return  成功返回true,失败返回false,意味着当操作进行时同步状态的当前值不是expect
**/
protected final boolean compareAndSetState(int expect, int update) { //方法被final修饰,不允许被重写
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}


队列是一个FIFO的双向队列

//注释

 * <pre>
 *      +------+  prev +-----+       +-----+
 * head |      | <---- |     | <---- |     |  tail
 *      +------+       +-----+       +-----+
 * </pre>
//code
private transient volatile Node head;
private transient volatile Node tail;

.3.4


package java.util.concurrent.locks;
import java.util.concurrent.TimeUnit;
import java.util.Collection;

public class ReentrantLock implements Lock, java.io.Serializable {


  //首先,就是我们最经常使用的构造方法,默认使用的都是非公平锁。
  //NofairSync,就是不公平锁的意思。
  public ReentrantLock() {
        sync = new NonfairSync();
    }

    //这里,你也可以构造一个公平锁出来,传递true参数即可。
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }


    //下边这么长,是一个内部类Sync。
    //我们的公平锁,非公平锁都是集成Sync,自然里边的方法都可以用。
    //很长,不用看,看下边的公平锁和非公平锁就可以。
    abstract static class Sync extends AbstractQueuedSynchronizer {
    
		//非公平锁就是这个,非公平锁类里判断能不能获取锁就是通过这个方法搞的。
		//将非公平锁的这个方法,与公平锁的这个方法一对比。
		//其意自现。
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }


 	//非公平锁
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
        这里可以看到这个方法compareAndSetState()
        比较并改变状态,简称CAS,这个和CAS思想是一样的。
        看看state是不是0,是0的话就修改状态为1.
        并获取锁,设置锁线程为当前自己线程。**/

		//非公平锁的lock方法
		//如果执行成功,则获取锁,失败,则执行acquire(1)方法。
		//acquire()方法在AbstractQueuedSynchronizer内部。
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }
        
		//这里非公平锁调用了父类Sync里的方法,判断是否可以进入获取锁。
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

   //公平锁
    static final class FairSync extends Sync {

		//上锁,修改状态state为1。
        final void lock() {
            acquire(1);
        }
		//在多线程竞争锁的时候,谁能获取小红的欢心,获取锁就是这个方法。
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
            	//非公平锁那里的判断是两个
            	//公平锁这里的判断是三个判断
            	//相比非公平锁,这里多了一个判断,判断一下自己本线程是否在等待队列的首部。
            	//另外两个操作一样,都是CAS操作以及获取锁操作。
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }

  
    //这里是ReentrantLock的方法,可以看到它使用的是内部类Sync的方法。
    //所以使用ReentrantLock时,下边很多方法都是Sync的,其实就是使用的Sync。
    //默认使用的都是非公平锁的lock()方法。
    //
    public void lock() {
        sync.lock();
    }

7.

**ReentrantLock** 和 **Synchronized**的对比?

为什么使用 ConcurrentHashMap

线程安全的有HashTable与加HashMap synchronized 的或者 加Lock 或者用Collection.Sychronized 作一个同步操作为什么要使用ConcurrentHashMap

HashTable是直接对里面的方法加了一个 Synchronized,也就是加一个对象锁并发效率极其低。但 ConcurrentHashMap 1.8后的数据结构变成了同样的 数组+链表+红黑树线程安全是使用了乐观锁+sychronized,它只会锁住,我目前获取到 Entry 所在节点的一个值。

Sychronized 锁升级

一开始是无锁,有线程来了就升级为偏向锁,测试线程id指向当前线程,然后执行同步代码块,如果又来了线程,判断当前线程id是否为测试线程id。如果不是就升级为轻量级锁也就是cas乐观锁,乐观锁有一个比较交换的过程,如果没有设置成功,会进行自旋,自旋到一定次数就会升级成重级锁 sychronized,这样保证了它的一个性能问题。

详细解释

反射影响性能

反射会在先在方法区内看看这个类有没有被加载过,如果没有的话会有一个类加载的过程,会在这里影响性能

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值