JAVA SE基础
1.ArrayList、LinkedList、Vector的特性及存储性能(持续更新)
- ArrayList和Vector都继承了AbstractList,前者线程不安全,后者线程安全
- ArrayList基于数组结构实现,查找速度快,插入和删除速度慢,LinkedList则相反,因为其是基于双向链表实现
- ArrayList通过实现AbstractList,能够随机访问,而LinkedList实现的是AbstractSequentialList,仅能顺次访问
2.HashMap、Hashtable、ConcurrentHashMap、LinkedHashMap(持续更新)
- HashMap线程不安全,Hashtable对每个操作都进行同步,线程安全;前者可以存在一个null的key,value可以为null,后者不能为null
3.简述JVM内存结构中的heap和stack(持续更新)
- heap即堆内存,存放对象数据,自动增加容量,存取较慢,线程共享
- stack即栈内存,存放方法的局部变量、基本数据类型、对象引用、方法调用登,线程独占
4.GC是什么? 为什么要有GC?
- GC是Garbage Collection的缩写,即垃圾回收
- 这里所谓的垃圾,指的是那些不再被使用的对象,JVM的垃圾回收机制使得开发人员从无聊、容易犯错的手动释放内存资源的过程中解放出来。
- 开发人员可以更加专注的进行业务功能的开发,而资源回收的工作交由更加专业的垃圾回收机制自动完成
5.关于String的知识点
- String s = new String("xyz"); 创建了几个String Object? :
创建了2个String对象,首先传入的"xyz"是一个String对象,然后new的过程又创建了一个String对象
6.abstract的方法是否可以是static的?是否可以是synchronized的?
- 不能是static的:抽象方法是不能分配内存的,而static的方法在类加载时就已经分配内存,所以不能共存
- 不能是synchronized的:方法同步的时候需要获取该方法所属实例对象的同步锁,但是abstract的方法是没有所属实例对象的,所以无法synchronized
7.lock与Synchronize的区别?lock的基本使用方法及特性(持续更新)
- Lock是一个接口,需要进行同步操作时,可实例化一个Lock(ReentrantLock),当前线程即占有这个Lock对象锁,而不像Synchronized那样需要制定持有某个特定的对象锁,这样能够避免Synchronized容易出现的死锁情况
- 线程无法自动释放Lock,需要手动unlock(通常在finally)中,因此如果不手动释放的话也会造成死锁
- Lock对象通过.lock()方法让线程占有锁,也可以通过lock.tryLock(long timeout, TimeUnit unit)方法来指定获取锁的时间,如果超时则返回false,放弃获取锁(也就放弃执行某同步方法)
- Lock实现同步的方式也是CAS机制,ReentrantLock中执行lock()操作的是内部抽象类Sync的实现,ReentrantLock实例化时默认是采用非公平锁(NonfairSync)这个实现:
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
可以看出,首先采用CAS机制来修改对象头中的锁标志位,CAS机制有三个重要参数:内存位置(V)、预期原值(A)和新值(B), 其中0就是期望的标志位原值A,1就是期望的标志位新值B;最终会调用一个native方法compareAndSwapInt()来使用CPU的CAS机制进行CAS操作,来不断尝试获取锁
- Lock的CAS机制是一种典型的乐观锁机制,可以避免线程进入阻塞状态造成线程上下文切换降低性能;但是锁竞争太激烈的话,反而会造成性能低下;
- Lock的Condition对象用法:使用Condition对象可以实现传统的wait()和notify()的目的,用Condition实现生产者-消费者模式:
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 阻塞栈,用于存放Mantou对象
*/
public class SyncStack {
volatile int index = 0;//栈内当前Mantou数量,需保证线程可见
Lock lock = new ReentrantLock();//初始化锁
Condition condition = lock.newCondition();//初始化Condition对象用于线程间通信
Mantou[] mantous;
public SyncStack(int size) {
//初始化栈内容器的大小
mantous = new Mantou[size];
}
/**
* 将Mantou放入栈中,每放一个,index就增加一个,然后将等待池中的线程唤醒
* 执行该方法前若检测若index已满则执行await,执行push()的线程进入Lock的等待池
* @param mantou
*/
public void push(Mantou mantou){
//获取同步锁
lock.lock();
try{
while(index == this.mantous.length){
condition.await();
}
this.mantous[index] = mantou;
index++;
condition.signalAll();
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
/**
* 将栈顶的Mantou拿出来,每拿一个,index就减少一个,然后将等待池中的线程唤醒
* 执行该方法前若检测index为空则执行await,执行get()的线程进入Lock等待池
* @return
*/
public Mantou get(){
lock.lock();
try{
while(index == 0){
//设置等待时间,一旦超时就返回false,退出该方法
if(!condition.await(30, TimeUnit.SECONDS)){
return null;
}
}
index--;
condition.signalAll();
return this.mantous[index];
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
return null;
}
}
public class Mantou {
int id;
String owner;
public Mantou(int id, String owner){
this.id = id;
this.owner = owner;
}
@Override
public String toString() {
return owner + "的" +String.valueOf(id);
}
}
/**
* 生产者线程,持续调用push方法向阻塞栈放入Mantou对象
*/
public class Producer implements Runnable{
SyncStack syncStack;
public Producer(SyncStack syncStack) {
this.syncStack = syncStack;
}
@Override
public void run() {
//持续放入8个Mantou
for(int i = 0; i<8; i++){
Mantou mantou = new Mantou(i, Thread.currentThread().getName());
syncStack.push(mantou);
System.out.println(Thread.currentThread().getName()+":生产了" + mantou + "号馒头!!!");
try{
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
/**
* 消费者线程,持续调用get方法从阻塞栈取出Mantou对象
*/
public class Consumer implements Runnable{
SyncStack syncStack;
public Consumer(SyncStack syncStack) {
this.syncStack = syncStack;
}
@Override
public void run() {
Mantou mantou;
//一旦获取Mantou超时,则退出
while((mantou = syncStack.get())!=null) {
System.out.println(Thread.currentThread().getName() + ":拿到了" + mantou + "号馒头!!!");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ":靠!居然没有了!艹!走了!");
}
}
测试:
public static void main(String[] args){
SyncStack syncStack = new SyncStack(10);
Consumer consumer = new Consumer(syncStack);
Producer producer = new Producer(syncStack);
Thread consumerThread1 = new Thread(consumer);
Thread consumerThread2 = new Thread(consumer);
Thread consumerThread3 = new Thread(consumer);
Thread producerThread1 = new Thread(producer);
Thread producerThread2 = new Thread(producer);
consumerThread1.setName("消费者1");
consumerThread2.setName("消费者2");
consumerThread3.setName("消费者3");
producerThread1.setName("生产者甲");
producerThread2.setName("生产者已");
producerThread1.start();
producerThread2.start();
consumerThread1.start();
consumerThread2.start();
consumerThread3.start();
跟传统的wait和notifyAll方式大同小异
8. 反射中Class.forName()和ClassLoader.loadClass()的区别
反射中,Class.forName()和ClassLoader的方法loadClass()都可以通过类名加载类并返回类的Class对象,但是两者有一些区别:
- Class.forName()实际调用的是forName0(className, true, ClassLoader.getClassLoader(caller), caller),这个过程默认将进行类加载->链接->初始化这一系列过程
- ClassLoader的方法loadClass()实际调用的是ClassLoader.loadClass(className,false)这个方法,第二个参数表示不进行链接及初始化,仅仅进行类加载
- 所有如果加载某个类需要进行链接及初始化操作,就需要调用Class.forName()这个方法来获取Class对象