java面试题总结

Java面试题

一、面向对象的三个特征
封装、继承,多态,有时候会有抽象
封装:字面意思就是包装的意思,是指利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体,数据被保护在抽象数据类型的内部,尽可能的隐藏内部的细节,只保留有一些对外接口使之与外部发生练习,即用户无需知道对象内部的细节,但可以通过该对象对外的提供的接口来访问该对象,对于封装而言,一个对象他所封装的是自己的属性和方法,所以他是不需要依赖其他对象就可以完成自己的操作,封装的优点有:
1、良好的封装能够减少耦合
2、类内部的结构可以自由修改
3、可以对成员进行更精确的控制。
4、隐藏信息,实现细节

public class User implements Serializable {
	//对属性的封装、姓名。年龄,性别都是这个人的私有属性
    private Integer uid;
    private String uname;
    private String usex;
    private Integer uage;
    private Integer proid;

	//setter()、getter()是该对象对外开放的接口
    public Integer getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
    }

    public String getUname() {
        return uname;
    }

    public void setUname(String uname) {
        this.uname = uname;
    }

    public String getUsex() {
        return usex;
    }

    public void setUsex(String usex) {
        this.usex = usex;
    }

    public Integer getUage() {
        return uage;
    }

    public void setUage(Integer uage) {
        this.uage = uage;
    }

    public Integer getProid() {
        return proid;
    }

    public void setProid(Integer proid) {
        this.proid = proid;
    }
}

继承
继承是使用已经存在的类的定义作为建立新类的技术,新类的定义可以增加新的数据或者是新的功能,,也可以使用父类的功能,但不能选择性地继承父类。通过使用继承我们能够非常方便的使用以前的代码,能够大大的提高开发的效率
继承时需要注意的是:
1、子类拥有父类的非private的属性和方法
2、子类可以拥有自己的属性和方法,即就是子类可以对父类进行拓展
3、子类可以使用自己的方式实现父类的方法
但对于构造器而言,只能是被调用,而不能是被继承,而调用父类的构造方法我们使用super()即可,构建过程是从父类“向外”扩散的,也就是从父类开始向子类一级一级完成构建的,我们并没有显示的使用super()来调用父类构造器,编译器会默认给子类调用父类的构造器但是调用父类的构造器也是有前提的,即就是父类有默认的构造器。如果父类没有默认构造器,我们就要必须显示的使用super()来调用父类的构造器,否则编译器会报错:即就是“无法找到符合父类形式的构造器”
对于继承而言,子类会默认调用父类的构造器,但是如果没有默认的父类构造器,子类必须要显示指定父类的构造器,而且必须是在子类构造器当中做的第一件事(放置于构造器第一行).
多态:
多态即就是指程序中定义的引用变量所指向的集体的类型和通过该引用变量发出的方法调用在编程时不确定,而是在程序运行期间才确定,即就是一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在程序运行期间才能确定,因为在程序运行时才能确定具体的类,这样,不用修改程序的源代码就可以让引用变量绑定到各种不同的类实现上,从而导致该引用变量调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这个就是多态
实现多态必须存在的条件
1、要有继承关系
2、方法需要重写
3、父类的引用指向子类的对象(向上转型)
抽象
当父类知道了子类应该包含啥样的方法,但无法确定子类如何实现这些方法,在分析事物时,会发现事物的共性,将共性抽出,实现的时候,就会有这样的情况,方法功能声明相同,但方法功能主题不同,这时,将方法声明抽取出,此时,该方法就是一个抽象方法.
抽象的特点:
1、抽象类和抽象方法都需要被abstract修饰,抽象方法一定要定义在抽象类中
2、抽象类不能直接创建对象,因为调用抽象方法每一意义
3、只有覆盖了抽象类中所有的抽象方法后,其子类才可以创建对象否则该子类还是一个抽象类
4、之所以继承抽象类,更多的是在思想,是面对共性类型操作会更简单
抽象类的注意事项
抽象类一定是个父类,因为是不断抽取而来的
抽象类中可以不定义抽象方法,其存在的意义就是不让该类创建对象,方法可以直接让子类去使用
抽象关键字 abstract 不可以和以下关键字共存:
private:私有的方法子类是无法继承到的,也不存在覆盖,如果 abstract 和 private 一起使用修饰方法, abstract 既要子类去实现这个方法,而 private 修饰子类根本无法得到父类这个方法,互相矛盾
final:final 修饰的类不能被继承,而抽象类一定是父类
static:static 修饰的表示静态的,不能被修改的,但可以直接被类所调用,而abstract修饰的是抽象的,即没有方法实体,也不能直接被调用
二、==和equals的区别是什么?
通俗点讲:==是看看左右是不是一个东西.equals是看看左右两边是不是长得一样.
专业术语来说:
1、==是判断两个变量或对象实例是不是指向同一个内存空间。equals是判断两个变量或实例所指向的内存空间的值是不是相等
2、==是指对内存地址进行比较。equals是对字符串的内容进行比较
3、==指引用是否相同,equals指的是值是否相等、
三、对象的相等与指向他们的引用相等,两者有什么不同
对象的相等,比的是内存中存放的内容是否相等,而引用相等比较的是他们指向的内存地址是否相等、
四、构造方法有哪些特征
1、名字与类名相等
2、没有返回值,但不能用void声明构造函数
3、生成类的对象时自动执行,无需调用。
五、创建一个对象用什么运算符?对象实体与对象引用有什么不同?
创建一个对象用的是new运算符,new创建对象实例(对象实例在堆内存中),对象引用指向对象实例(对象引用存放在栈内存中)一个对象引用可以指向0个或一个对象实例;一个对象可以有n个引用指向它
六、java中的volatile和synchronized有什么区别?
volatile是JVM中用于保证可见性和有序性的轻量级同步机制
它主要有两个作用,一是保证被修饰变量的可见性,也就是多线程读写操作时,能被其它线程感知到,在读取变量时会强制将主内存的变量读取到自己的工作内存中,写入变量时又会强制自己的新值刷新回主内存。二是在于他阻止指令重排序,即在我们所熟知的双检测单例中,instance必须要用volatile修饰,原因是new SingleTest 时,有三个步骤(字节码):
1、分配一块内存
2、在内存上初始化SingleTon对象
3、把这块内存地址返回值赋值给 instance

但是经过编译器的优化,2、3的顺序有可能是颠倒的,也就是说可能你拿到的instance可能还没有被初始化,访问instance的成员变量就可能发生空指针异常,而volatile可以阻止这种情况的发生。

public class SingleTest {
    private static volatile SingleTest instance = null;

    public static SingleTest getInstance(){
        if (instance == null){
            synchronized (SingleTest.class){
                if (instance == null){
                	//下面一行代码的字节码:  21和24可能指令重排,其他线程在synchronized外面可能return还未创建完成的实例                
                    //17: new #3  创建对象,将对象引用入栈
                    //20: dup     复制一份对象引用 
                    //21: invokespecial #4  利用一个对象引用,调用构造方法
                    //24: putstatic #2 利用一个对象引用,赋值给 instance
                    instance = new SingleTest();
                }
            }
        }
        return instance;
    }
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                System.out.println(SingleTest.getInstance());
            }).start();
        }
    }
}

	1、**volatile**本质是告诉JVM当前变量在寄存器(工作内存),中的值是不确定的,需要从主存中读取;**synchronized**则是锁定当前变量,只有当前线程才可以访问该变量,其他线程则被阻塞
	2、**volatile**仅能使用在变量级别,**synchronized**则可以使用在变量,方法,和类级别的。
	3、**volatile**仅能实现变量的修改可见性,并不能保证原子性,而**synchronized**则可以保证变量的修改可见性和原子性
	4、**volatile**不会造成线程的阻塞**synchronized**可能会造成线程的阻塞
	5、**volatile**标记的变量不会被编译器优化(即就是代码执行的顺序不会被改变);**synchronized**标记的变量可以被编译器优化。

六、接口和抽象类的区别是什么?
java提供和支持的创建抽象接口类和接口,他们的实现有共同点,而不同点在于:
1、接口中所有的方法隐含的都是抽象的,而抽象类则可以同时包含抽象和非抽象的方法。
2、类可以实现多个接口,但是只能继承一个抽象类
3、类可以不实现抽象类和接口的所有方法,当然,在这种情况下,类也必须是得声明是抽象的.
4、抽象类可以在不提供接口方法实现的情况下实现接口
5、java接口中声明的变量默认都是final修饰符修饰的,抽象类可以包含非final的变量
6、java接口中的成员函数默认是public修饰的,抽象类的成员函数可以是private,protected或者是public 。
7、接口是绝对抽象的,不可以被实例化,抽象类也不可以被实例化
七、java语言支持的8中基本数据类型是哪些?什么是自动拆装箱
byte(1字节)、boolean(1字节)
short(2字节)、char(2字节)
int(4字节)、float(4字节)
long(8字节)、double(8字节)
在这里插入图片描述
自动装箱就是java编译器在基本数据类型和对应的对象包装类型之间做的一个转化,比如:将int转化为Integer,double转化为Double等等,反之就是自动拆箱
java支持的数据类型有两种:
一种是基本数据类型【byte char short boolean int long float double】
另一种是引用类型,如:String等,其实也是对象的引用,JVM中虚拟机栈中存放的是对象的地址,创建的对象实质是在堆中,通过地址来找到堆中的对象的过程,即为引用类型,自动装箱就是java编译器在基本数据类型和对应的对象包装类型之间的转化,即int转换为Integer,自动拆箱是Integer调用其方法将其转化为int的过程
八、“static”关键字是什么意思?Java中是否可以覆盖(override)一个private或者是static的方法?
1、"static"关键字表明一个成员变量或者是成员方法可以在没有所属的类的实例变量的情况下被访问.
2、Java中static方法不能被覆盖,因为方法覆盖是基于运行时动态绑定的,static方法跟类的任何实例都不相关,所以概念上不适用
3、Java中也不可以覆盖private修饰的方法,以外private修饰的方法和变量只能在当前类中使用,如果是其他的类继承当前类是不能被访问到private的变量或者是方法的,当然更不能将其覆盖.
九、两个对象的 hashCode()相同,则 equals()也一定为 true,对吗
hashCode():hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个 int 整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在 JDK 的 Object 类中,这就意味着 Java 中的任何类都包含有 hashCode() 函数。另外需要注意的是: Object 的 hashcode 方法是本地方法,也就是用 c 语言或 c++ 实现的,该方法通常用来将对象的 内存地址 转换为整数之后返回。

当你把对象加入 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时也会与其他已经加入的对象的 hashcode 值作比较,如果没有相符的 hashcode,HashSet 会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用 equals() 方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度。

如果两个对象相等,则 hashcode 一定也是相同的。两个对象相等,这两个对象分别调用 equals 方法都返回 true。但是,两个对象有相同的 hashcode 值,它们也不一定是相等的 。因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖。equals()相等的的两个等价对象因为hashCode不同,所以在hashmap中的table数组的下标不同,从而这两个对象就会同时存在于集合中,在调用hashmap集合中的方法时就会出现逻辑的错误,也就是,你的equals()方法也“白白”重写了。

因为 hashCode() 所使用的杂凑算法也许刚好会让多个对象传回相同的杂凑值。越糟糕的杂凑算法越容易碰撞,但这也与数据值域分布的特性有关(所谓碰撞也就是指的是不同的对象得到相同的 hashCode。

我们刚刚也提到了 HashSet,如果 HashSet 在对比的时候,同样的 hashcode 有多个对象,它会使用 equals() 来判断是否真的相同。也就是说 hashcode 只是用来缩小查找成本
十、在写实体类时,为什么要实现Serializable接口?
最重要的两个原因是:
  1、将对象的状态保存在存储媒体中以便可以在以后重新创建出完全相同的副本;
  2、按值将对象从一个应用程序域发送至另一个应用程序域。

实现 serializable接口的作用是就是可以把对象存到字节流,然后可以恢复。所以你想如果你的对象没实现序列化怎么才能进行 网络传输呢,要 网络传输就得转为字节流,所以在分布式应用中,你就得实现序列化,如果你不需要分布式应用,那就没那个必要实现序列化。
十一、String str="i"与 String str=new String(“i”)一样吗?
String str=“i”;

这句话的意思是把“i”这个值在内存中的地址赋给str,如果再有String str3=“i”;那么这句话的操作也是把“i”这个值在内存中的地址赋给str3,这两个引用的是同一个地址值,他们两个共享同一个内存。

而String str2 = new String(“i”);

则是将new String(“i”);的对象地址赋给str2,需要注意的是这句话是新创建了一个对象。如果再有String str4= new String(“i”);那么相当于又创建了一个新的对象,然后将对象的地址值赋给str4,虽然str2的值和str4的值是相同的,但是他们依然不是同一个对象了。

需要注意的是:String str=“i”; 因为String 是final类型的,所以“i”应该是在常量池。

而new String(“i”);则是新建对象放到堆内存中。 在这里插入图片描述
在这里插入图片描述
十二、JVM内存模型
在这里插入图片描述
堆内存:又分为新生代老年代其比例为新生代:老年代 = 1:2( 该值可以通过参数 –XX:NewRatio 来指定)
新生代又可以分为Eden区From Survivor区To Survivor区,其比例为Eden:from(幸存者1区):to(幸存者2区)=8:1:1(可以通过参数 –XX:SurvivorRatio 来设定)。
1、堆是被所有的线程所共享的区域,是在虚拟机启动的时候创建的.
2、几乎所有的new出来的对象都是存在与heap中,Java对象的实例以及数组都在堆上分配,堆内存是Java虚拟机所管理内存中最大的一块。
3、堆内存又分为年轻代和老年代,而我们平时所说的垃圾回收,主要回收的就是堆区。 在这里插入图片描述
方法区:(永生代)
1、方法区用于存储虚拟机加载的类信息常量静态变量是各个线程所共享的内存区域
2、在JDK8之前的HotSpot JVM,区域叫做“永久代(permanent generation)”。永久代是一片连续的堆空间,在JVM启动之前通过在命令行设置参数-XX:MaxPermSize来设定永久代最大可分配的内存空间,默认大小是64M(64位JVM默认是85M)。
3、随着JDK8的到来,JVM不再有 永久代(PermGen)。但类的元数据信息(metadata)还在,只不过不再是存储在连续的堆空间上,而是移动到叫做“Metaspace”的本地内存(Native memory)。
栈内存:栈内存又可分为Java虚拟机栈本地方法栈.
虚拟机栈(JVM Stack) —— 我们平常说的栈
java虚拟机栈是线程私有,生命周期与线程相同。创建线程的时候就会创建一个java虚拟机栈。虚拟机执行java程序的时候,每个方法都会创建一个栈帧,栈帧存放在java虚拟机栈中,通过压栈出栈的方式进行方法调用。
栈帧又分为一下几个区域:局部变量表、操作数栈、动态连接、方法出口等。
平时我们所说的变量存在栈中,这句话说的不太严谨,应该说局部变量存放在java虚拟机栈的局部变量表中。
java的8中基本类型的局部变量的值存放在虚拟机栈的局部变量表中,如果是引用型的变量,则只存储对象的引用地址。
本地方法栈(Native Stack)
本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务
JVM内存参数设置
在这里插入图片描述
-Xms设置堆的最小空间大小。
-Xmx设置堆的最大空间大小。
-Xmn:设置年轻代大小
-XX:NewSize设置新生代最小空间大小。
-XX:MaxNewSize设置新生代最大空间大小。
-XX:PermSize设置永久代最小空间大小。
-XX:MaxPermSize设置永久代最大空间大小。
-Xss设置每个线程的堆栈大小
-XX:+UseParallelGC:选择垃圾收集器为并行收集器。此配置仅对年轻代有效。即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集。
-XX:ParallelGCThreads=20:配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。
十三、SpringMVC的工作原理
tomcat启动时,也是先加载web.xml,找到SpringMVC的前端总控制器DispatcherServlet,并且通过DispatcherServlet来加载相关的配置文件信息
当浏览器发出一个请求之后,首先找到DispatcherServlet,通过这个控件器和代码中的requestMapping注解找到对应的controller中的方法,参数直接通过方法参数接收就可以,然后调用service、dao、操作数据库,返回数据。如果页面跳转,controller的方法可以返回ModelAndView和String类型,再通过Spring-mvc配置文件中的视图解析器找到对应的页面。数据可以用requestModelAndView返回页面,如果是ajax请求,可以直接返回List或者对象,加上responseBody注解,进行返回。
十四、锁机制
一、SQL锁
悲观锁:就是考虑问题很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁。
实现:SQL语句后边加上 for update
例子:select id,name, from table for update
乐观锁:与悲观锁正好相反,就是每次去拿数据的时候都会认为别人不会修改数据,所以不会上锁。
实现:在表里加上一个version
例子:select max(nub),version from table
Update table set nub=nub+1,version=version+1 where id=id and version = verison
二、方法锁
方法锁主要包含:synchronized锁和lock
区别:《1》、Lock是一个接口,而synchronized是java中的关键字
《2》、synchronized当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,不能够响应中断(释放:1、执行完线程线程自动释放2、发生异常jvm让线程释放)((比如调用sleep方法)),其好处是不会导致死锁现象发生。Lock锁,可以不让等待的线程一直无限期的等待下去,只会等待一定的时间或者是相应中断。但Lock锁在发生异常时,如果没有主动通过unLock()去释放锁,则可能会造成死锁现象,因此使用Lock时必须在try{}catch{}块中进行,需要在finally块中释放锁。
《3》、通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。在性能上来说,如果资源竞争不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(就是有大量线程同时竞争),此时Lock的性能就要远远优于synchronized。所以说,在合适的场景选择合适的锁。
在并发量比较小的情况下,使用synchronized是个不错的选择,但是在并发量比较高的情况下,其性能下降很严重,此时ReentrantLock(可重入锁,唯一实现了Lock接口的类)是个不错的方案。

十五:索引
索引可以理解为数据的查询目录,建索引的目的就是 提高对表的查询速度;没有索引时,查询时全表检索,有了索引就可以根据索引快速查找需要的数据;
但是索引也不能乱建,因为索引需要维护,会导致增删改的效率降低。会使数据的维护变的复杂,影响开发的效率,索引也会占用数据库的物理空间;所以我们一般在项目的开发阶段、测试阶段、试运行阶段都很少去创建索引,因为有了索引,系统bug造成垃圾数据特别不好删除。只有在项目正式上线后才去增加索引,以提高项目运行速度。索引我们一般都创建在经常作为查询条件的字段、排序的字段和作为关联关系的字段上边。尽量避免在大文本字段、数据量比较小的字段(比如性别),增删改性能大于检索性能的字段上边;另外,有些情况,即使增加了索引,索引也不会生效,比如:索引字段使用了不等于(!=或者<>)符号,用了函数、进行了运算,使用了is null或is not null,
和不匹配的数据类型进行比较、like查询时两边都用了%等;还有一个要注意的地方是,如果在多个字段上建立联合索引,那么组合索引的第一个列被where子句引用时,索引才会起作用。因为想要使用索引增加查询效率,必然要牺牲增删改效率,为了解决这个问题,我们经常对数据库做主从复制,读写分离。同时创建两个数据库,一主一从,两个数据库数据完全一致,主的数据库用来进行写的操作,操作后数据库会自动把数据同步到从的数据库,从的数据库用来执行读的操作。这样我们建立索引时,就可以只在读的数据库创建就可以了。这样就索引即能增加查询效率,有不影响增删改效率。这样做了之后,我们还可以对他进一步优化,比如数据库引擎的优化,主数据库因为执行增删改操作,所以用事务型引擎Innodb,
读的数据库,不需要事务,就可以用效率更高的MyIASM引擎。同时根据实际情况,也可以配置一主多从或者多主多从。
索引的创建常用的有2中方式:
CREATE 【UNIQUE】INDEX index_name ON table_name (column_list);
或者ALTER TABLE table_name ADD INDEX index_name (id,name);
修改用:ALTER TABLE table_name REBUILD INDEX index_name (column_list);
删除用:DROP INDEX index_name ON talbe_name
或者:ALTER TABLE table_name DROP INDEX index_name
查看用:select * from all_indexes where table_name=‘student’;
Conlum_list中多个字段用”,”号分割。

十六、22数据库优化应该从哪些方法考虑?
1、数据库选型:首先应该从数据库的选型来看,首先是关系型数据库,目前最流行的是mysql和Oracle,mysql更适合于中小型项目,安装使用比较简单,免费开源。Oracle适合于大型项目,安装包比较大,使用比较耗内存,收费,但是效率高、可靠性好,使用行级锁,比mysql的表级锁粒度更细,对并发性的支持要好的多。NOSQL数据库(比如:mongdb),事务性比较差,但效率高,主要适用于数据量比较大,但关系型要求不是很强的数据,比如评论数据。缓存数据库(比如:redis),由于保存在内存中,所以速度特别快,但是不适应持久化保存。所以缓存数据库一般作为应用和数据库的中间层来使用。
**2、设计规范:**其次应该从数据库设计规范来考虑。好的规范,可以是数据库运行效率更高,优化更方便。以mysql为例;
由于mysql对大小写在windows和linux上敏感型不一致,为了避免麻烦,我们一般建议数据库名、表名、字段名都用小写+下划线命名;
数据库名最好于项目名称一致。
表名最好采用:“数据库名_表名”的方式;
字段名最后见名知意,不要太长;
字段选用合适的字段类型,比如姓名可以用varchar(10),而不要用默认的长度255,日期最好用date或datetime或时间戳。金额最好用decimal,而不是double、float等。
关联表以外的表最好都有主键id、创建时间、创建人、最后修改时间、最后修改人字段。
数据库设计时,以三大范式为参考,但是必要时,可以使用冗余;
对于预期数据量特别大的表,可能没有都会有很大增长的表,要考虑水平分表,比如系统日志表,可以考虑表名中加入月份,每月生成新表。
对于字段比较多的表,比如企业信息表,可以考虑纵向分表,把表分成两张1对1的表。把经常查询的字段放在一张表里,不经常用到的和大文本字段放在另外一张表里。
开发阶段,介意每个表只有主键,不应该包含其他外键。因为开发过程中,系统不稳定,可能出现很多垃圾数据,有了外键,数据的清理会给我们开发照成很多不便。要求开发人员用代码维护表之间的关系,这样更能增加代码的健壮性。
**3、索引:**开发测试完成以后,一定要记得给数据库添加索引,增加sql的执行效率。添加索引也要注意,索引应该添加到经常作为查询条件或者排序字段、或者作为外键的列上。而那些不经常查询的字段、大文本字段、可选值比较少的字段(比如性别)等不需要创建索引。另外,sql中的不等于、is null、函数、like两边同时使用%等,会使索引失效,应该尽量避免使用;
**4、配置:**数据库的配置优化(my.ini),比如:最大连接数、最大缓存数、最大线程数、物理内存等;
5、读写分离、主从复制优化。因为一般查询多、修改少,索引可以增加查询效率,但会影响修改效率。所以做了读写分离以后,可以把索引只建立在读的数据库上。并且查询不需要事务,所以可以用非事务行引擎;
6、数据库集群,数据库压力过大时,用集群来分担压力是个很不错的方案;
十七、多线程高并发问题:
处理高并发一般有
分流
队列
两种分式。
分流可以用集群实现队列可以用activeMQ消息队列或者线程锁消息队列就是把所有的线程消息集中管理,排成队一个一个的进行。线程锁的方式在生成订单编号时用到过,就是在生成编号的方法上加上syn开头的同步锁。
问题java开启线程的几种方式
有继承Thread类,
实现runable接口。但是我在基本上这样用的比较少,大
部分时间都是开启一个spring的线程池。ThreadPoolTaskExecutor

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值