## JAVA 三大特性
- 封装:**隐藏**对象的属性和实现**细节**,仅对外公开接口
- 继承: 子类继承父类的特征和行为,使得子类对象具有父类的属性和方法,使子类具体与父类相同的行为
- 多态:同一个接口,不同的实例有不同的实现,即父类的引用指向子类对象
## Synchorized和ReentryLock的区别
- Synchorized是一个关键字,存在于JVM层面,而ReentryLock依赖于API
- 两者都是可重入锁
- 加锁和解锁方面:Synchorized是通过进入/退出对象监视器来实现的,ReentryLock需要通过lock对象的lock加锁,并且在finally语句块中显示的释放,lock.unlock()
- ReentryLock的一些高级功能
- 等待可中断:在等待获取锁的时间内ReentryLock是可中断的,而Synchorized不可以
- **是否阻塞**:ReentryLock获取锁的过程是非阻塞的,Synchorized是阻塞的
- 可以实现公平锁(通过构造函数传入fair的值是ture或false可以控制是公平还是非公平)
- **等待通知机制的实现**:Synchorized通过对象的**wait()**和notify()来实现,而ReentryLock则使用Condition类来实现,通过lock.newCondition()来创建Condition对象,Condition可以实现选择性通知,更加灵活,**一个ReentryLock可以同时绑定多个condition对象**
- 性能:经过不断优化,Synchorized的性能和ReentryLock已经基本持平
## Synchorized和volatile的区别
- **作用域**
- volatile仅用于修饰变量
- Synchorized可作用于变量,方法,代码块,类
- volatile不保证**原子性**
- **是否阻塞**:volatile不会阻塞
- **虚拟机优化**:volatile不会被虚拟机优化(保证顺序性)
## 有关synchorized
- 会阻塞线程
- 不可中断
- 作用范围
- **性能**
### 同步问题
当多线程对类的**成员变量**进行修改时,多线程修改的是同一个变量。此时就会出现并发问题,而局部变量是线程私有的,不会出现线程安全问题
### synchorized的作用范围
- 修饰代码块
- 这段代码块称为静态代码块,作用范围是{}的部分,作用对象是调用这段代码块的对象
- 修饰普通方法
- 这个方法被称为同步方法,作用范围是整个方法,作用对象是调用这个方法的对象
- 修饰静态方法
- 修饰的是这个方法,作用对象是这个**类的所有对象**
- 修饰类
- 作用范围是synchorized(className.class)括号中的类,作用对象是括号中类的所有对象
总而言之:synchorized作用在方法还是对象上,如果对象是非静态的,那么锁的是对象,如果作用对象是静态或者是类的话,作用对象是类的所有对象,每个对象只有一个锁lock与之关联,谁拿到锁就可以运行它控制的那段代码,实现同步是要很大的系统开销作为代价的,甚至有可能造成死锁,所以要避免无用的同步
## 关于synchorized的优化
- **自旋锁**
- 线程状态之间状态转变需要很大的系统开销,在应用中,锁往往指存在很短的时间,自旋锁的思想是在一个线程请求锁的过程中执行忙循环(自旋)一段时间,如果在这段时间内能够获取锁,就可以避免进入阻塞状态,在1.6以后引入了适应性自旋,自旋的次数不再固定,由上一次的自旋次数以及锁的拥有者的状态来决定
- **锁消除**
- 将一些不会发生线程安全问题的所进行消除,例如String类
- **锁粗化**
- 将一些要对同一对象进行频繁加锁/解锁的操作,将锁的范围扩大到整个操作的外部,这样就只需要进行一次操作
- **偏向锁**
- 偏向锁的思想是偏向于第一个获取到锁的线程,这个线程在获取到锁之后再也不需要进行同步操作,甚至连CAS操作也不需要(**无争用的时候取消cas**)
- **轻量级锁**
- 从1.6以后锁有了四个状态:**无状态,偏向锁状态,轻量级锁状态和重量级锁状态**,轻量级锁的目的是避免重量级锁使用**互斥量**的开销,如果两条以上的线程同时竞争同一个锁,轻量级锁不再有用,将膨胀称为重量锁
## wait和sleep的区别
- **对象锁**
每个对象都有一个锁来控制同步访问,Synchronized关键字可以和对象的锁交互,来实现同步方法或同步块。
sleep()方法正在执行的线程主动让出CPU(然后CPU就可以去执行其他任务),在sleep指定时间后CPU再回到该线程继续往下执行(注意:sleep方法只让出了CPU,而并不会释放同步资源锁!!!);
wait()方法则是指当前线程让自己暂时退让出同步资源锁,以便其他正在等待该资源的线程得到该资源进而运行,只有调用了notify()方法,之前调用wait()的线程才会解除wait状态,可以去参与竞争同步资源锁,进而得到执行。(注意:notify的作用相当于叫醒睡着的人,而并不会给他分配任务,就是说notify只是让之前调用wait的线程有权利重新参与线程的调度);
wait()方法需要调用notify方法唤醒,唤醒以后才能参与对象锁的竞争
- 使用范围
sleep()方法可以在任何地方使用;
wait()方法则只能在同步方法或同步块中使用;(synchorized)
释放锁有两种方式:
(1)程序自然离开监视器的范围,即离开synchronized关键字管辖的代码范围
(2)在synchronized关键字管辖的代码内部调用监视器对象的wait()方法
- 不同类对象
sleep()是线程线程类(Thread)的方法,调用会暂停此线程指定的时间,但监控依然保持,不会释放对象锁,到时间自动恢复;
wait()是Object的方法,调用会放弃对象锁,进入等待队列,待调用notify()/notifyAll()唤醒指定的线程或者所有线程,才会进入锁池,不再次获得对象锁才会进入运行状态;
- 唤醒方式
在调用sleep()方法后,线程放弃cpu资源进入阻塞态,等时间片一到线程又进入就绪态
调用wait()方法后线程阻塞,直到调用对象的.notify()方法后才进入就绪状态
## String, String Builder 和String Buffer区别
**线程安全**:String Buffer
**快慢**:String Builder > String Buffer > String
String 慢的原因是:String为字符串常量(对String进行操作实际上是新创建了对象),而StringBuilder和StringBuffer均为字符串变量,即String对象一旦创建之后该对象是不可更改的,但后两者的对象是变量,是可以更改的
适用范围:
- 少量字符串拼接:String
- 单线程下大量字符串拼接:String Builder
- 多线程下大量字符串拼接:String Buffer
## 接口和抽象类的区别,如何选择?
- 方法抽象:接口比抽象类还要抽象,接口中的方法全都是抽象方法,而抽象类中的方法不一定是抽象方法
- **方法类型**:抽象类可以是私有的,非abstract的,但是必须实现,接口中不能是私有的,默认是public abstract 类型
- 继承:一个类只可以继承一个抽象类,但是可以实现多个接口
- **成员变量类型**:接口中默认是public static final,不能重新定义
如何选择?
- 如果要实现**多重继承**,那么必须使用接口
- 如果基本功能在**不断改变**,那么需要抽象类,因为如果使用接口,那么接口定义也要变
## 反射的基本概念,反射是否可以调用私有方法
JAVA反射:在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的**任意**属性和方法,并且能改变它的属性
反射是可以调用对象的私有方法的(只能调用自身,不能调用父类的),但是没有意义,破坏了JAVA的封装性
## JAVA8的新特性
- Lambda表达式
- 接口中可以有一个默认的实现方法,使用default关键字修饰
- Stream API,支持**函数式编程**
- Date Time API:加强对时间日期的处理
- Optinal类:用于解决空指针异常
- 新的JavaScript引擎
## finally语句是在什么时候执行的
- finally是在**return 语句之后,返回结果之前执行**的,先将return 的结果保存,所以函数的返回值是在finally之前就已经确定了
- finally中如果有return,则函数是在这里返回
- 无论如何finally都会执行,即使发生异常
## String不可变的原因
- String 类用final关键字修饰,所以不可被继承,不可被改变
- 内部使用char[]保存值,这个数组使用private final进行修饰,final保证char[]引用不变(因为数据还是可以改变的,数组引用是在栈上,但是数组元素是保存在堆上),private保证char[]不被暴露,所以内容也不可变
## String不可变的好处
- **线程安全**,final保证了线程安全,例如hashmap的key,或是string经常被作为参数传递
- 维护一个**字符串常量池**,不需要重复创建,节省时间和空间
## 同步和异步的区别
- 同步:一个线程在发起调用时,必须等待执行结果,在没有得到结果之前,不能进行后序操作
- 异步:发起调用之后,没有得到结果,调用者可以进行后序操作,一般调用结果是通过状态,通知,回调的方式通知调用者
一键复制
编辑
Web IDE
原始数据
按行查看
历史