字节二面 —— 什么是同步锁、死锁、乐观锁、悲观锁

 马上就要到金三银四佳季了,是找工作的好时候,小伙伴们一定要把握好时机,找到心仪的高薪工作。找工作就少不了面试,那我们从现在开始,多刷刷面试题,查缺补漏!!!

c19f2664af5646ed82170b1ca3efb141.png


目录

1. 面向对象的特征

2. Java的基本数据类型有哪些

3. JDK JRE JVM 的区别

4. 重载和重写的区别

5. Java中==和equals的区别

6. String、StringBuffer、StringBuilder三者之间的区别

7. 接口和抽象类的区别是什么

8. string常用的方法有哪些

扫描二维码关注公众号,回复: 13711294 查看本文章

9. 什么是单例模式?有几种?

10. 反射

11. Java的异常

12. BIO、NIO、AIO 有什么区别

13. 同步锁、死锁、乐观锁、悲观锁

14. 说一下 synchronized 底层实现原理

15. synchronized 和 volatile 的区别是什么

16. synchronized 和 Lock 有什么区别

17. 手写冒泡排序


1. 面向对象的特征

面向对象的特征:封装、继承、多态、抽象。

 封装:就是把对象的属性和行为(数据)结合为一个独立的整体,并尽可能隐藏对象的内部实现细节,就是把不想告诉或者不该告诉别人的东西隐藏起来,把可以告诉别人的公开,别人只能用我提供的功能实现需求,而不知道是如何实现的。增加安全性。

继承:子类继承父类的数据属性和行为,并能根据自己的需求扩展出新的行为,提高了代码的复用性。

多态:指允许不同的对象对同一消息做出相应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式(发送消息就是函数调用)。封装和继承几乎都是为多态而准备的,在执行期间判断引用对象的实际类型,根据其实际的类型调用其相应的方法。

抽象:表示对问题领域进行分析、设计中得出的抽象的概念,是对一系列看上去不同,但是本质上相同的具体概念的抽象。在Java中抽象用 abstract 关键字来修饰,用 abstract 修饰类时,此类就不能被实例化,从这里可以看出,抽象类(接口)就是为了继承而存在的。 


2. Java的基本数据类型有哪些

基本数据类型:byte,short,int,long,char,float,double,boolean

  数据类型 字节数 位数
整型 byte 1 8
short 2 16
int 4 32
long 8 64
浮点型 float 4 32
double 8 64
布尔型 boolean 1 8
字符型 char 2 16

3. JDK JRE JVM 的区别

JDK(Java Development Kit)是整个 Java 的核心,是java开发工具包,包括了 Java 运行环境 JRE、Java 工具和 Java 基础类库。

JRE(Java Runtime Environment)是运行 JAVA 程序所必须的环境的集合,包含java虚拟机和java程序的一些核心类库。

JVM 是 Java Virtual Machine(Java 虚拟机)的缩写,是整个 java 实现跨平台的最核心的部分,能够运行以 Java 语言写作的软件程序。


4. 重载和重写的区别

重载: 发生在同一个类中,方法名必须相同,参数类型不同.个数不同.顺序不同,方法返回值和访问修饰符可以不同,发生在编译时。

重写: 发生在父子类中,方法名.参数列表必须相同,返回值范围小于等于父类,抛出的异 常范围小于等于父类,访问修饰符范围大于等于父类;如果父类方法访问修饰符为 private 则子类就不能重写该方法。


5. Java中==和equals的区别

== 的作用
  基本类型:比较的就是值是否相同
  引用类型:比较的就是地址值是否相同
equals 的作用:
  引用类型:默认情况下,比较的是地址值。


注意:String、Integer、Date这些类库中equals被重写,比较的是内容而不是地址!


6. String、StringBuffer、StringBuilder三者之间的区别

String 字符串常量

String 中的String 类中使用 final 关键字修饰字符数组来保存字符串,private final char value[] ,String对象是不可变的,也就可以理解为常量,线程安全。


AbstractStringBuilder 是 StringBuilder 与 StringBuffer 的公共父类,定义了一些字符串的基本操作,如 expandCapacity、append、insert、indexOf 等公共方法。
StringBuffer 字符串变量(线程安全)

StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。


StringBuilder 字符串变量(非线程安全)

StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。


小结:

(1)如果要操作少量的数据用 String;

(2)多线程操作字符串缓冲区下操作大量数据用 StringBuffer;

(3)单线程操作字符串缓冲区下操作大量数据用 StringBuilder。


7. 接口和抽象类的区别是什么

实现:抽象类的子类使用 extends 来继承;接口必须使用 implements 来实现接口。

构造函数:抽象类可以有构造函数;接口不能有。

main 方法:抽象类可以有 main 方法,并且我们能运行它;接口不能有 main 方法。

实现数量:类可以实现很多个接口;但是只能继承一个抽象类。

访问修饰符:接口中的方法默认使用 public 修饰;抽象类中的方法可以是任意访问修饰符


8. string常用的方法有哪些

indexOf():返回指定字符的索引。

charAt():返回指定索引处的字符。

replace():字符串替换。

trim():去除字符串两端空白。

split():分割字符串,返回一个分割后的字符串数组。

getBytes():返回字符串的 byte 类型数组。

length():返回字符串长度。

toLowerCase():将字符串转成小写字母。

toUpperCase():将字符串转成大写字符。

substring():截取字符串。

equals():字符串比较。


9. 什么是单例模式?有几种?

单例模式:某个类的实例在 多线程环境下只会被创建一次出来。 单例模式有饿汉式单例模式、懒汉式单例模式和双检锁单例模式三种。

饿汉式:线程安全,一开始就初始化。

懒汉式:非线程安全,延迟初始化。

双检锁:线程安全,延迟初始化。


10. 反射

在 Java 中的反射机制是指在运行状态中,对于任意一个类都能够知道这个类所有的属性和方法;并且对于任意一个对象,都能够调用它的任意一个方法;这种动态获取信息以及动态调用对象方法的功能成为 Java 语言的反射机制


获取 Class 对象的 3 种方法 :

调用某个对象的 getClass()方法

Person p=new Person();

Class clazz=p.getClass();

调用某个类的 class 属性来获取该类对应的 Class 对象

Class clazz=Person.class;

使用 Class 类中的 forName()静态方法(最安全/性能最好)

Class clazz=Class.forName("类的全路径"); (最常用)


11. Java的异常

Throwable是所有Java程序中错误处理的父类,有两种资类:Error和Exception。 

 Error:表示由JVM所侦测到的无法预期的错误,由于这是属于JVM层次的严重错误,导致JVM无法继续执行,因此,这是不可捕捉到的,无法采取任何恢复的操作,顶多只能显示错误信息。

Exception:表示可恢复的例外,这是可捕捉到的。


 1.运行时异常:都是RuntimeException类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。
2.非运行时异常 (编译异常):是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。


常见的RunTime异常几种如下

NullPointerException - 空指针引用异常 
ClassCastException - 类型强制转换异常。 
IllegalArgumentException - 传递非法参数异常。 
ArithmeticException - 算术运算异常 
ArrayStoreException - 向数组中存放与声明类型不兼容对象异常 
IndexOutOfBoundsException - 下标越界异常 
NegativeArraySizeException - 创建一个大小为负数的数组错误异常 
NumberFormatException - 数字格式异常 
SecurityException - 安全异常 
UnsupportedOperationException - 不支持的操作异常


12. BIO、NIO、AIO 有什么区别

BIO:Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。

NIO:New IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。

AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。


13. 同步锁、死锁、乐观锁、悲观锁

同步锁:

当多个线程同时访问同一个数据时,很容易出现问题。为了避免这种情况出现,我们要保证线程同步互斥,就是指并发执行的多个线程,在同一时间内只允许一个线程访问共享数据。Java 中可以使用 synchronized 关键字来取得一个对象的同步锁。

死锁:

何为死锁,就是多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。

乐观锁:

总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_conditio机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。

悲观锁:

总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。


14. 说一下 synchronized 底层实现原理

synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性。

Java中每一个对象都可以作为锁,这是synchronized实现同步的基础:

  • 普通同步方法,锁是当前实例对象
  • 静态同步方法,锁是当前类的class对象
  • 同步方法块,锁是括号里面的对象

15. synchronized 和 volatile 的区别是什么

volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。

volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的。

volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性。

volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。

volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化。


16. synchronized 和 Lock 有什么区别

首先synchronized是java内置关键字,在jvm层面,Lock是个java类;

synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;

synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;

用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;

synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可);

Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。


17. 手写冒泡排序

public class Sort {
    public static void sort() {
        Scanner input = new Scanner(System.in);
        int sort[] = new int[10];
        int temp;
        System.out.println("请输入10个排序的数据:");
        for(int i = 0;i < sort.length;i++){
            sort[i] = input.nextInt();
        }
        for(int i = 0;i < sort.length - 1;i++){
            for(int j = 0;j < sort.length - i - 1;j++){
                if(sort[j] < sort[j+1]){
                    temp = sort[j];
                    sort[j] = sort[j+i];
                    sort[j+i] = temp;
                }
            }    
        }
        System.out.println(""排列后的顺序为:);
        for(int i = 0;i<sort.length;i++){
            System.out.print(sort[i]+"===");
        }
    }
}

 这篇文章如果对小伙伴们有帮助的话,希望点个赞支持一下~      十分感谢~

af5df094dca5421b8adabbb28e99e5e3.gif


猜你喜欢

转载自blog.csdn.net/weixin_42306958/article/details/122973102