java基础
java基础 目录 |
---|
1、java是一种什么语言,jdk,jre,jvm三者的区别 |
2、java 1.5之后的三大版本 |
3、java跨平台及其原理 |
4、java 语言的特点 |
5、什么是字节码,字节码的好处 |
6、java 和 c++ 的区别 |
7、java的三大特性 |
8、java中的基本数据类型和引用数据类型及其区别 |
9、switch(expr),expr支持哪些数据类型 |
10、int 和 Integer 有什么区别,怎么理解自动拆箱,自动装箱 |
11、计算2^3效率最高的方法是 |
12、Math.round(temp) |
13、float f=3.4;是否正确 |
14、short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;有错吗 |
15、java中的注释 |
16、java中的访问修饰符 |
17、重写与重载的区别 |
18、运算符 &和&&的区别 |
19、Java 有没有 goto |
20、this关键字的用法 |
21、super关键字的用法 |
22、java 的final 关键字 |
23、break ,continue ,return 的区别及作用 |
24、在 Java 中,如何跳出当前的多重嵌套循环 |
25、hashCode 与 equals |
26、抽象类和接口的区别是什么 |
27、什么是接口 |
28、静态变量与非静态变量的区别 |
29、值传递和引用传递的区别是什么 |
30、什么是反射 |
31、String 类中常用的方法 |
32、String 中的==和 equals 的区别 |
33、Java 中的 String,StringBuilder,StringBuffer 三者的区别 |
34、Java中final、finally和finalize的区别 |
35、Java里可不可以有多继承 |
36、HashMap 和 Hashtable 的区别 |
37、Map 集合有哪些实现类,分别具有什么特征 |
38、解决 hashmap 线程不安全问题 |
39、Hashmap 的底层实现原理 |
40、hash 碰撞怎么产生,怎么解决 |
41、HashMap 为什么需要扩容 |
42、 如何遍历 Map 集合 |
43、 ArrayList 与 LinkedList 区别 |
44、ArrayList 与 LinkedList 区别 |
45、如何使用的 List 集合来保证线程安全 |
46、IO 和 NIO 的区别 |
47、在 Java 中要想实现多线程代码有三种手段 |
48、Thread 类中的 start() 和 run() 方法有什么区别 |
49、Java 中 notify 和 notifyAll 有什么区别 |
50、Java 多线程中调用 wait() 和 sleep()方法有什么不同 |
51、什么是线程安全 |
52、Java中的 volatile 变量是什么 |
53、线程的状态 |
54、实现线程同步有三种方式 |
55、Java中的锁有几种方式 |
56、Lock的几个实现类 |
57、线程间通信的几种实现方式 |
58、synchronized 和 Lock 的区别和应用场景 |
59、为什么要用线程池 |
60、如何创建线程池 |
61、java中的异常体系 |
62、throw 和 throws 的区别 |
63、说出 5 个常见的异常 |
1、java是一种什么语言,jdk,jre,jvm三者的区别?
java是一种完全面向对象的编程语言,具有简单性、面向对象、分布式、健壮性、安全性、平台独立与可移植性、多线程、动态性等特点,它吸收了c++的优点,去掉了c++中多继承,指针等让人难于理解的概念。java语言采用Unicode编码标准。 JDK(Java Development Kit)是针对 Java 开发员的产品,是整个 Java 的核心,包括了 Java 运行环境 JRE、Java 工具和 Java 基础类库。 Java Runtime Environment(JRE)是运行 JAVA 程序所必须的环境的集合,包含 JVM 标准实现及 Java 核心类库。 JVM 是 Java Virtual Machine(Java 虚拟机)的缩写,是整个 java 实现跨平台的最核心的部分,能够运行以 Java 语言写作的软件程序。
java SE java标准版 java EE java企业版 java ME java微型版
所谓的跨平台就是java源码经过一次编译以后,可以在不同的操作系统上运行 原理:经过编译的 .class 文件运行在java虚拟机上,并非直接运行在操作系统上,只要安装满足不同操作系统的jvm即可。
1、面向对象。java 是面向对象语言,即满足面向对象的基本特征(封装,继承,多态)。封装是将 属性,方法等放在一个类中,体现了java语言的安全性,继承是子类继承了父类后便有了父类的特性,多态则是父类的引用指向子类的地址,执行不同的操作,继承和多态体现了java语言的灵活性,易扩展性。 2、跨平台。jvm实现java语言的跨平台。 3、支持网络编程。 4、支持多线程。 5、健壮性。java语言的强类型机制,异常处理机制,GC垃圾自动收集机制。
字节码:java 经过 javac 命令产生的 .class 文件就是字节码。 字节码的好处:1、在一定程度上解决了解释性语言效率低下的问题。2、不针对特定的机器,保留了解释性语言的可移植性。
java和c++都是面向对象语言。因此都有面向对象的基本特性封装,继承,多态。它们的区别如下: 1、java不提供指针来直接访问内存,程序内存更加安全。 2、java中是单继承,c++中支持多继承。 3、java中有内存管理机制,无需程序员手动释放内存。
封装: 把方法、变量封装在类中,提高代码的安全性 继承: java中为单继承,提高代码的重用性 多态: 多态就是同一个类或者接口,使用不同的实例因而执行不同的操作,提高代码的灵活性
- 8种基本数据类型
说明 | 所占内存大小(字节) | 取值范围 | 默认值 | |
---|---|---|---|---|
byte | java中最小的数据类型 | 1 | − 2 7 {-2^7} −27~ 2 7 {2^7} 27-1 | 0 |
short | 短整型 | 2 | − 2 15 {-2^{15}} −215~ 2 15 {2^{15}} 215-1 | 0 |
int | 整型 | 4 | − 2 31 {-2^{31}} −231~ 2 31 {2^{31}} 231-1 | 0 |
long | 长整型 | 8 | − 2 63 {-2^{63}} −263~ 2 63 {2^{63}} 263-1 | 0L |
float | 单精度 | 4 | -3.40E+38 ~ +3.40E+38 | 0 |
double | 双精度 | 2 | -1.79E+308 ~ +1.79E+308 | 0 |
char | 字符型 | 2 | 0~65535 | null |
boolean | 布尔型 | 1 | true,false | false |
- 引用数据类型
类,接口类型,数组类型,枚举类型,注解类型。更多教程请访问码农之家
- 基本数据类型与引用数据类型的区别
基本数据类型在被创建时,会在栈上分配空间,直接将之存储在栈中。而引用数据类型在被创建时,首先会在栈上分配空间,将其引用存在栈空间中,然后在堆中开辟内存,值存放在堆内存中,栈中的引用指向堆中的地址。
在java5以前,expr支持 byte,short,int,char 四种数据类型,在java5以后,又多了枚举enum类型,java7又增加了string类型,到目前并比支持long类型。
10、int 和 Integer 有什么区别,怎么理解自动拆箱,自动装箱?
int 是基本数据类型,默认值是0 integer是引用类型,是int 的包装类,默认值是 null 自动拆箱:将包装类型自动转化为对应的基本数据类型。 自动装箱:将基本类型自动转化为对应的引用类型。
计算2^3效率最高的方法是:2<<(3-1)
四舍五入的原理是在原来的参数上加0.5,然后进行向下取整。
不正确。3.4是双精度类型,赋值给float需要强制类型转换,float f=(float)3.4,可以写成 float f=3.4F。
14、short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;有错吗?
short s1 = 1; s1 = s1 + 1 不正确。因为 1是int类型,因此 s1+1 也是int类型,在执行 s1=s1+1 时,需要将int类型的s1+1赋值给short类型的s1,大转小可能会有精度损失,无法显示转化。 short s1 = 1; s1 += 1 正确。因为 s1+=1 相当于s1=(short)(s1+1),存在隐含的强制类型转换。
定义:注释是用来解释说明程序的文字。分为: 单行注释:// 注释的文字 多行注释:/* 注释的文字 / 文档注释:/* 注释的文字 **/
java中的访问修饰符有:public,private,protected,以及不写(默认)。
重写: 至少发生在两个类中,并且类与类具有继承或者实现关系,表示子类中的方法具有与父类方法中完全相同的方法名称,返回值,参数。子类中的方法覆盖父类的方法,体现了多态性。 重载: 发生在同一个类中,多个方法名称相同,参数类型,个数和顺序不同的方法发生重载现象,与返回值无关。
&:无论左边true还是false,右边也会进行判断。 &&:如果左边为false,有边就不会进行判断,因此&&比&效率高。 注意:逻辑或运算符(|)和短路或运算符(||)的差别也是如此。
goto 是 Java 中的保留字,在目前版本的 Java 中没有使用。
1.普通的直接引用,this相当于是指向当前对象本身。 2.形参与成员名字重名,用this来区分:
public Person(String name, int age) {
this.name = name;
this.age = age;
}
3.调用本类的构造函数:
class Person{
private String name;
private int age;
public Person() {
}
public Person(String name) {
this.name = name;
}
public Person(String name, int age) {
this(name);
this.age = age;
}
}
1.普通的直接引用。 2.调用父类中与子类重名的方法。 2.调用父类的构造函数。
在java中,final关键字可以修饰类,变量和方法。被final修饰后以下特点: final修饰类:final修饰的类不能被继承。 final修饰变量:final修饰的变量是常量,不能改变。 final修饰方法:final修饰的方法不能被重写。
23、break ,continue ,return 的区别及作用?
break:跳出当前循环 continue:结束本次循环,进行下次循环 return:返回
在外面的循环语句前定义一个标号
ok:
for(int i=0;i<10;i++){
for(int j=0;j<10;j++){
system.out.println("i="+i+",j="+j);
if(j==5)break ok;
}
}
如果两个对象equals()方法相等则它们的hashCode返回值一定要相同,如果两个对象的hashCode返回值相同,但它们的equals()方法不一定相等。 两个对象的hashCode()返回值相等不能判断这两个对象是相等的,但两个对象的hashcode()返回值不相等则可以判定两个对象一定不相等。
接口中的方法都是抽象的,抽象类中可以有抽象方法,也可以有非抽象方法。 在jdk1.8以后接口中也可以有用defaule关键字修饰的普通方法
接口是一种规范,java中的接口:interface
静态变量 | 非静态变量 | |
---|---|---|
调用方式 | 静态变量只能通过 “ 类名.变量名 ” 调用 | 非静态变量通过实例化对象名调用 |
共享方式 | 静态变量是全局变量,被类的所有实例化对象共享 | 非静态变量是局部变量,不共享 |
相互访问方式 | 静态变量无法访问非静态变量 | 非静态变量可以访问静态变量 |
值传递: 在方法的调用过程中,实参把它的实际值传递给形参,此传递过程就是将实参的值复制一份传递到函数中。 引用传递: 引用传递弥补了值传递的不足,如果传递的数据量很大,直接复过去的话,会占用大量的内存空间,而引用传递就是将对象的地址值传递过去,函数接收的是原始值的首地址值。在方法的执行过程中,形参和实参的内容相同,指向同一块内存地址,也就是说操作的其实都是源数据,所以方法的执行将会影响到实际对象。
JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。
方法 | 说明 |
---|---|
split() | 把字符串分割成字符串数组 |
indexOf() | 从指定字符提取索引位置 |
append() | 追加字符或字符串 |
trim() | 去掉字符串两端的空格 |
replace() | 替换 |
hashCode() | 返回字符串的哈希值 |
subString() | 截取字符串 |
equals() | 比较字符串是否相等 |
length() | 获取字符串长度 |
concat() | 将指定字符串连接到此字符串的结尾 |
"=="比较的是两个字符串的内存地址。 "equals"比较的是两个字符串的实际值
33、Java 中的 String,StringBuilder,StringBuffer 三者的区别?
String: 字符串常量,底层用 final 关键字修饰,底层实际在维护 char 类型的字符数组,当每次对String进行改变时,都需要生成一个新的String对象,然后将指针指向一个新的对象。
//底层用 final 关键字修饰,底层实际在维护 char 类型的字符数组
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
}
StringBuilder: 字符串变量,线程安全,用于多线程操作 StringBuffer : 字符串变量,非线程安全,用于单线程操作
34、Java中final、finally和finalize的区别?
final: 修饰符,java 中的关键字。可用于修饰类,变量,方法,有最终的意思。
修饰的对象 | 说明 |
---|---|
final 修饰类 | 表明该类不能被其他类所继承,但要注意:final类中所有的成员方法都会隐式的定义为final方法。 |
final 修饰变量 | final成员变量表示常量,只能被赋值一次,赋值后其值不再改变 |
final 修饰方法 | final 修饰的方法不能被重写 |
finally: finally 是在异常里经常用到的, 就是 try 和 cach 里的代码执行完以后,必须要执行的方法,我们经常在 finally 里写一些关闭资源的方法,比如说关闭数据库连接,或者关闭 IO 流. finalize: finalize是方法名,java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作
java中不允许多继承,比如类A不能同时继承类B和类C,若要有次类需求,考虑用接口。
HashMap 和 Hashtable是Map接口的实现类,它们大体有一下几个区别:
- 继承的父类不同。HashMap是继承自AbstractMap类,而HashTable是继承自Dictionary类。
- 线程安全性不同。Hashtable 中的方法是Synchronize的,而HashMap中的方法在缺省情况下是非Synchronize的。Hashtable 是现成安全的,HashMap是非线程安全的。
- key和value是否允许null值。Hashtable中,key和value都不允许出现null值。但是如果在Hashtable中有类似put(null,null)的操作,编译同样可以通过,因为key和value都是Object类型,但运行时会抛出NullPointerException异常,这是JDK的规范规定的。HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。当get()方法返回null值时,可能是 HashMap中没有该键,也可能使该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey()方法来判断。
实现类 | 特征 |
---|---|
HashMap | 线程不安全的键值对集合,允许 null 值,key 和 value 都可以 |
HashTable | 线程安全的键值对集合,不允许 null 值,key 和 value 都不可以 |
TreeMap | 能够把它保存的记录根据键排序,默认是按升序排序 |
- Collections.synchronizedMap() 方法
- java.util.concurrent.ConcurrentHashMap 类
在JDK1.6,JDK1.7中,HashMap采用位桶+链表实现,即使用链表处理冲突,同一hash值的键值对会被放在同一个位桶里,当桶中元素较多时,通过key值查找的效率较低。 而JDK1.8中,HashMap采用位桶+链表+红黑树实现,当链表长度超过阈值(8),时,将链表转换为红黑树,这样大大减少了查找时间。 当我们创建 hashmap 时 会先创建一个数组,当我们用 put 方法存数据时,先根据 key 的 hashcode 值计算出 hash 值,然后用这个哈希值确定在数组中的位置,再把 value 值放进去,如果这个位置本来没放 东西,就会直接放进去,如果之前就有,就会生成一个链表,把新放入的值放在头部,当用 get 方法取值时,会先根据 key 的 hashcode 值计算出 hash 值,确定位置,再根据 equals 方法从该位置上的链表中取出该 value 值。
对象Hash的前提是实现equals()和hashCode()两个方法,那么HashCode()的作用就是保证对象返回唯一hash值,但当两个对象计算值一样时,这就发生了碰撞冲突。如下将介绍如何处理冲突,当然其前提是一致性hash。 解决hash碰撞有以下几种方法:
- 开放地址法
开放地执法有一个公式:Hi=(H(key)+di) MOD m i=1,2,…,k(k<=m-1) 其中,m为哈希表的表长。di 是产生冲突的时候的增量序列。如果di值可能为1,2,3,…m-1,称线性探测再散列。如果di取1,则每次冲突之后,向后移动1个位置.如果di取值可能为1,-1,2,-2,4,-4,9,-9,16,-16,…kk,-kk(k<=m/2),称二次探测再散列。如果di取值可能为伪随机数列。称伪随机探测再散列。
- 再哈希法
当发生冲突时,使用第二个、第三个、哈希函数计算地址,直到无冲突时。缺点:计算时间增加。比如上面第一次按照姓首字母进行哈希,如果产生冲突可以按照姓字母首字母第二位进行哈希,再冲突,第三位,直到不冲突为止。
- 链地址法(拉链法)
将所有关键字为同义词的记录存储在同一线性链表中。如下:
当 hashmap 中的元素个数超过数组大小loadFactor 时,就会进行数组扩容,loadFactor 的默认值为 0.75,也就是说,默认情况下,数组大小为 16,那么当hashmap 中元素个数超过 160.75=12 的时候,就把数组的大小扩展为216=32,即扩大一倍。然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作,所以如果我们已经预知 hashmap 中元素的个数,那么预设元素的个数能够有效的提高 hashmap 的性能。比如说,我们有 1000 个元素new HashMap(1000), 但是理论上来讲 new HashMap(1024)更合适,不过上面annegu 已经说过,即使是 1000,hashmap 也自动会将其设置为 1024。 但是newHashMap(1024)还不是更合适的,因为 0.751000<1000, 也就是说为了让 0.75*size>1000, 我们必须这样 newHashMap(2048)才最合适,避免了resize 的问题。
先获取 Map 中的 key 的 set 集合 map.keySet(); 通过遍历 key 集合,获取 value 值。Map.get(key)先获取 Entry 集合 map.entrySet(); 遍历 entrySet 分别获取 key value。
43、 ArrayList 与 LinkedList 区别?
ArrayList 使用数组方式存储数据,所以根据索引查询数据速度快,而新增或者 删除元素时需要设计到位移操作,所以比较慢。 LinkedList 使用双向链接方式存储数据,每个元素都记录前后元素的指针, 所以插入、删除数据时只是更改前后元素的指针指向即可,速度非常快,然后通过下标查询元素时需要从头开始索引,所以比较慢,但是如果查询前几个元素或 后几个元素速度比较快。 ArrayList 与 LinkedList 都是线程不安全的。
44、 Java中的ArrayList的初始容量和容量分配?
ArrayList是经常会被用到的,一般情况下,使用的时候会像这样进行声明: List arrayList = new ArrayList(); 如果像上面这样使用默认的构造方法,初始容量被设置为10。当ArrayList中的元素超过10个以后,会重新分配内存空间,使数组的大小增长到16。 可以通过调试看到动态增长的数量变化:10->16->25->38->58->88->…
也可以使用下面的方式进行声明: List arrayList = new ArrayList(4); 将ArrayList的默认容量设置为4。当ArrayList中的元素超过4个以后,会重新分配内存空间,使数组的大小增长到7。 可以通过调试看到动态增长的数量变化:4->7->11->17->26->…
那么容量变化的规则是什么呢?请看下面的公式: ((旧容量 * 3) / 2) + 1
1、使用 Vector 2、使用 Collections 中的方法 synchronizedList 将 ArrayList 转换为线程安全的 List 3、使用 java.util.current 包下的 CopyOnWriteArrayList(推荐)
这个NIO是JDK1.7以后有的 ,它们俩的主要区别是 :io 是面向流是阻 塞 io,nio 是面向缓 冲,非阻塞的 io; io 话每次从流中读取一 个多个字节 ,直到读取完所有的字节 ,没有缓存到 任何地方 .nio 读取的是数据是有缓存 ,就是说他读取的数据是 在缓冲里读的 . 另外的话 ,java 中的各种 io 是阻塞的 .就是说一个线程调用 read 或 者 write()时 ,这个线程就已经被阻塞了,直到读取到一 些数据为止 ,或者是完全写入。在这个过程中不能干其他的事情 . nio 的非阻塞模式 ,当发送一个读取数据的请求的时候 ,如果没有读取到可用的数据 ,就什么也不会 获取 ,且不会让线程阻塞写也是这样。非阻塞的IO的空闲时间可用用来做其他的操作所以,一个单 独的非阻塞线 程可以管理 多个输入和输出通道,另外 NIO 还有一 个 selector(选 择 器 ),它是可以管理多个输入输出的通道.
一种是继承 Thread 类 另一种就是实现 Runnable 接口 最后一种就是实现 Callable 接口 (第四种也是实现 callable 接口,只不过有返回值而已)
48、Thread 类中的 start() 和 run() 方法有什么区别?
start()方法被用来启动新创建的线程,而且 start()内部调用了 run()方法,这和直接调用 run()方法的效果不一样。当你调用 run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程。
49、Java 中 notify 和 notifyAll 有什么区别?
notify()方法不能唤醒某个具体的线程,所以只有一个线程在等待的时候它 才有用武之地。而 notifyAll()唤醒所有线程并允许他们争夺锁确保了至少有一 个线程能继续运行。
50、Java 多线程中调用 wait() 和 sleep()方法有什么不同?
Java 程序中 wait 和 sleep 都会造成某种形式的暂停,它们可以满足不同的需要。wait()方法用于线程间通信,如果等待条件为真且其它线程被唤醒时它会释放锁,而 sleep()方法仅仅释放 CPU 资源或者让当前线程停止执行一段时间,但不会释放锁。
多个线程同时运行一段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。同一个实例对象在被多个线程使用的情况下也不会出现计算失误,也是线程安全的,反之则是线程不安全的。
Volatile: 一个共享变量(类的成员变量、类的静态成员量)被volatile修饰之后,那么就具备了两层语义: a.保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。 b.禁止进行指令重排序。但是它并不能保证操作的原子性。 应用场景:在只涉及可见性,针对变量的操作只是简单的读写(保证操作的 原子性)的情况下可以使用volatile来解决高并发问题,如果这时针对变量的操作是非原子的操作,这时如果只是简单的i++式的操作,可以使用原子类atomic类来保证操作的原子性(采用CAS实现),如果是复杂的业务操作,那么舍弃volatile,采用锁来解决并发问题(synchronized或者Lock)。
实线程一般具有五种状态,即创建、就绪、运行、阻塞、终止。
- 新建( new ):新创建了一个线程对象。
- 可运行( runnable ):线程对象创建后,其他线程(比如 main 线程)调用了该对象的start ()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获 取 cpu 的使用权 。
- 运行( running ):可运行状态( runnable )的线程获得了 cpu 时间片( timeslice ),执行程序代码。
- 阻塞( block ):阻塞状态是指线程因为某种原因放弃了 cpu 使用权,也即让出了 cpu timeslice ,暂时停止运行。直到线程进入可运行( runnable )状态,才有机会再次获得 cpu timeslice 转到运行( running )状态。阻塞的情况分三种: (一). 等待阻塞:运行( running )的线程执行 o.wait ()方法,JVM 会把该线程放 入等待队列( waitting queue )中。 (二). 同步阻塞:运行( running )的线程在获取对象的同步锁时, 若该同步锁被别的线程占用,则 JVM 会把该线程放入锁池( lock pool )中。 (三). 其他阻塞: 运行( running )的线程执行 Thread . sleep ( long ms )或 t.join ()方法,或者发出了 I / O 请求时,JVM 会把该线程置为阻塞状态。当 sleep ()状态超时、 join ()等待线程终止或者超时、或者 I / O 处理完毕时,线程重新转入可运行( runnable )状态。
- 死亡( dead ):线程 run ()、 main () 方法执行结束,或者因异常退出了 run ()方法,则该线程结束生命周期。死亡的线程不可再次复生。
1、同步代码块:在代码块上加上“synchronized”关键字的话,则此代码块就称为同步代 码块。
同步代码块格式:
synchronized(监视对象){
需要同步的代码 ;
}
解释:监视对象有三种:对象、String、.class 文件(只要是不变的对象都可以做监 视对象) 2、同步方法
同步方法定义格式:
synchronized 方法返回值 方法名称(参数列表){
}
在方法上加 synchronized,是把当前对象做为监视器
3、同步锁
Lock lock = new ReentrantLock();(可以在类中直接 new)
lock.lock(); 中间的代码块进行加锁 lock.unlock();
- Synchronized
- Lock
Synchronized的局限性: 1).如果这个获取锁的线程由于要等待IO或者其他原因(比如调用sleep方法)被阻塞了,但是又没有释放锁,其他线程便只能干巴巴地等待。(不能主动释放锁) 2).当有多个线程读写文件时,读操作和写操作会发生冲突现象,写操作和写操作会发生冲突现象,但是读操作和读操作不会发生冲突现象如果多个线程都只是进行读操作,所以当一个线程在进行读操作时,其他线程只能等待无法进行读操作。(不分情况,一律锁死)
ReentrantLock是一个可重入的互斥锁,又被称为“独占锁” ReadWriteLock,顾名思义,是读写锁。它维护了一对相关的锁 ——“读取锁”和“写入锁”,一个用于读取操作,另一个用于写入操作。他的两个实现类读锁readerLock和写锁writerLock。
1、使用 volatile 关键字。基于 volatile 关键字来实现线程间相互通信是使用共享内存的思想,大致意思就是多个线程同时监听一个变量,当这个变量发生变化的时候 ,线程能够感知并执行相应的业务。这也是最简单的一种实现方式 2、使用Object类的wait() 和 notify() 方法。Object类提供了线程间通信的方法:wait()、notify()、notifyaAl(),它们是多线程通信的基础,而这种实现方式的思想自然是线程间通信。
注意: wait和 notify必须配合synchronized使用,wait方法释放锁,notify方法不释放锁
58、synchronized 和 Lock 的区别和应用场景?
1、Lock 是接口,而 synchronized 是 Java 中的关键字,synchronized 是内置的语言实现; 2、synchronized 在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而 Lock 在发生异常时,如果没有主动通过 unLock()去释放锁,则很可能造成死锁现象,因此使用 Lock 时需要在 finally 块中释放锁; 3、Lock 可以让等待锁的线程响应中断,而 synchronized 却不行,使用synchronized 时,等待的线程会一直等待下去,不能够响应中断; 4、通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到。 5、Lock 可以提高多个线程进行读操作的效率。 6、Lock 能完成 Synchronized 所实现的所有功能在性能上来说,如果竞争资源不激烈,Synchronized 要优于 Lock,而当竞争资源非常激烈时(即有大量线程同时竞争),此时 Lock 的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。
创建线程要花费昂贵的资源和时间,如果任务来了才创建线程那么响应时间会变长,而且一个进程能创建的线程数 有限。为了避免这些问题,在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程。从JDK1.5 开始,JavaAPI 提供了 Executor 框架让你可以创建不同的线程池。比如单线程池,每次处理一个 任务;数目固定的线程池或者是缓存线程池(一个适合很多生存期短的任务的程序的可扩展线程池)。
1.线程池都是通过线程池工厂创建,再调用线程池中的方法获取线程,再通过线程去执行任务方法。 Executors:线程池创建工厂类 2.自己根据创建线程池的需求来 new 对象(使用) 注意:线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors 返回的线程池对象的弊端如下: 1)FixedThreadPool 和 SingleThreadPool: 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。 2)CachedThreadPool 和 ScheduledThreadPool: 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。 建议自己通过 new 关键字创建 newThreadPoolExecutor
1、throws 用在函数上,后面跟的是异常类,可以跟多个;而 throw 用在函数内,后面跟的 是异常对象。 2、throws 用来声明异常,让调用者只知道该功能可能出现的问题,可以给出预先的处理方 式;throw 抛出具体的问题对象,执行到 throw,功能就已经结束了,跳转到调用者,并将具体的问题对象抛给调用者。也就是说 throw 语句独立存在时,下面不要定义其他语句,因为执行不到。 3、throws 表示出现异常的一种可能性,并不一定会发生这些异常;throw则是抛出了异常, 执行 throw 则一定 抛出了某种异常对象。 4、两者都是消极处理异常的方式,只是抛出或者可能抛出异常,但是不会由函数去处理异常,真正的处理异常由函数的上层调用处理。
1、NullpointException:空指针异常 null 值 导致 2、IOExceptionIO 异常 IO 流常见编译异常 3、SQLExceptionSQL 拼写异常,mybatis 中的 sql 拼写异常 4、ClassNotFoundException 类找不到异常 一般为 jar 包引入失败或者忘写 spring 注解 5、ClassCastException 类型转换异常