JAVA SE基礎(chǔ)
1.ArrayList、LinkedList、Vector的特性及存儲(chǔ)性能(持續(xù)更新)
- ArrayList和Vector都繼承了AbstractList,前者線程不安全,后者線程安全
- ArrayList基于數(shù)組結(jié)構(gòu)實(shí)現(xiàn),查找速度快,插入和刪除速度慢,LinkedList則相反,因?yàn)槠涫腔陔p向鏈表實(shí)現(xiàn)
- ArrayList通過(guò)實(shí)現(xiàn)AbstractList,能夠隨機(jī)訪問(wèn),而LinkedList實(shí)現(xiàn)的是AbstractSequentialList,僅能順次訪問(wèn)
2.HashMap、Hashtable、ConcurrentHashMap、LinkedHashMap(持續(xù)更新)
- HashMap線程不安全,Hashtable對(duì)每個(gè)操作都進(jìn)行同步,線程安全;前者可以存在一個(gè)null的key,value可以為null,后者不能為null
3.簡(jiǎn)述JVM內(nèi)存結(jié)構(gòu)中的heap和stack(持續(xù)更新)
- heap即堆內(nèi)存,存放對(duì)象數(shù)據(jù),自動(dòng)增加容量,存取較慢,線程共享
- stack即棧內(nèi)存,存放方法的局部變量、基本數(shù)據(jù)類型、對(duì)象引用、方法調(diào)用登,線程獨(dú)占
4.GC是什么? 為什么要有GC?
- GC是Garbage Collection的縮寫,即垃圾回收
- 這里所謂的垃圾,指的是那些不再被使用的對(duì)象,JVM的垃圾回收機(jī)制使得開發(fā)人員從無(wú)聊、容易犯錯(cuò)的手動(dòng)釋放內(nèi)存資源的過(guò)程中解放出來(lái)。
- 開發(fā)人員可以更加專注的進(jìn)行業(yè)務(wù)功能的開發(fā),而資源回收的工作交由更加專業(yè)的垃圾回收機(jī)制自動(dòng)完成
5.關(guān)于String的知識(shí)點(diǎn)
- String s = new String("xyz"); 創(chuàng)建了幾個(gè)String Object? :
創(chuàng)建了2個(gè)String對(duì)象,首先傳入的"xyz"是一個(gè)String對(duì)象,然后new的過(guò)程又創(chuàng)建了一個(gè)String對(duì)象
6.abstract的方法是否可以是static的?是否可以是synchronized的?
- 不能是static的:抽象方法是不能分配內(nèi)存的,而static的方法在類加載時(shí)就已經(jīng)分配內(nèi)存,所以不能共存
- 不能是synchronized的:方法同步的時(shí)候需要獲取該方法所屬實(shí)例對(duì)象的同步鎖,但是abstract的方法是沒(méi)有所屬實(shí)例對(duì)象的,所以無(wú)法synchronized
7.lock與Synchronize的區(qū)別?lock的基本使用方法及特性(持續(xù)更新)
- Lock是一個(gè)接口,需要進(jìn)行同步操作時(shí),可實(shí)例化一個(gè)Lock(ReentrantLock),當(dāng)前線程即占有這個(gè)Lock對(duì)象鎖,而不像Synchronized那樣需要制定持有某個(gè)特定的對(duì)象鎖,這樣能夠避免Synchronized容易出現(xiàn)的死鎖情況
- 線程無(wú)法自動(dòng)釋放Lock,需要手動(dòng)unlock(通常在finally)中,因此如果不手動(dòng)釋放的話也會(huì)造成死鎖
- Lock對(duì)象通過(guò).lock()方法讓線程占有鎖,也可以通過(guò)lock.tryLock(long timeout, TimeUnit unit)方法來(lái)指定獲取鎖的時(shí)間,如果超時(shí)則返回false,放棄獲取鎖(也就放棄執(zhí)行某同步方法)
- Lock實(shí)現(xiàn)同步的方式也是CAS機(jī)制,ReentrantLock中執(zhí)行l(wèi)ock()操作的是內(nèi)部抽象類Sync的實(shí)現(xiàn),ReentrantLock實(shí)例化時(shí)默認(rèn)是采用非公平鎖(NonfairSync)這個(gè)實(shí)現(xiàn):
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
可以看出,首先采用CAS機(jī)制來(lái)修改對(duì)象頭中的鎖標(biāo)志位,CAS機(jī)制有三個(gè)重要參數(shù):內(nèi)存位置(V)、預(yù)期原值(A)和新值(B), 其中0就是期望的標(biāo)志位原值A(chǔ),1就是期望的標(biāo)志位新值B;最終會(huì)調(diào)用一個(gè)native方法compareAndSwapInt()來(lái)使用CPU的CAS機(jī)制進(jìn)行CAS操作,來(lái)不斷嘗試獲取鎖
- Lock的CAS機(jī)制是一種典型的樂(lè)觀鎖機(jī)制,可以避免線程進(jìn)入阻塞狀態(tài)造成線程上下文切換降低性能;但是鎖競(jìng)爭(zhēng)太激烈的話,反而會(huì)造成性能低下;
- Lock的Condition對(duì)象用法:使用Condition對(duì)象可以實(shí)現(xiàn)傳統(tǒng)的wait()和notify()的目的,用Condition實(shí)現(xiàn)生產(chǎn)者-消費(fèi)者模式:
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 阻塞棧,用于存放Mantou對(duì)象
*/
public class SyncStack {
volatile int index = 0;//棧內(nèi)當(dāng)前Mantou數(shù)量,需保證線程可見
Lock lock = new ReentrantLock();//初始化鎖
Condition condition = lock.newCondition();//初始化Condition對(duì)象用于線程間通信
Mantou[] mantous;
public SyncStack(int size) {
//初始化棧內(nèi)容器的大小
mantous = new Mantou[size];
}
/**
* 將Mantou放入棧中,每放一個(gè),index就增加一個(gè),然后將等待池中的線程喚醒
* 執(zhí)行該方法前若檢測(cè)若index已滿則執(zhí)行await,執(zhí)行push()的線程進(jìn)入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();
}
}
/**
* 將棧頂?shù)腗antou拿出來(lái),每拿一個(gè),index就減少一個(gè),然后將等待池中的線程喚醒
* 執(zhí)行該方法前若檢測(cè)index為空則執(zhí)行await,執(zhí)行g(shù)et()的線程進(jìn)入Lock等待池
* @return
*/
public Mantou get(){
lock.lock();
try{
while(index == 0){
//設(shè)置等待時(shí)間,一旦超時(shí)就返回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);
}
}
/**
* 生產(chǎn)者線程,持續(xù)調(diào)用push方法向阻塞棧放入Mantou對(duì)象
*/
public class Producer implements Runnable{
SyncStack syncStack;
public Producer(SyncStack syncStack) {
this.syncStack = syncStack;
}
@Override
public void run() {
//持續(xù)放入8個(gè)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()+":生產(chǎn)了" + mantou + "號(hào)饅頭!??!");
try{
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
/**
* 消費(fèi)者線程,持續(xù)調(diào)用get方法從阻塞棧取出Mantou對(duì)象
*/
public class Consumer implements Runnable{
SyncStack syncStack;
public Consumer(SyncStack syncStack) {
this.syncStack = syncStack;
}
@Override
public void run() {
Mantou mantou;
//一旦獲取Mantou超時(shí),則退出
while((mantou = syncStack.get())!=null) {
System.out.println(Thread.currentThread().getName() + ":拿到了" + mantou + "號(hào)饅頭?。。?);
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ":靠!居然沒(méi)有了!艸!走了!");
}
}
測(cè)試:
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("消費(fèi)者1");
consumerThread2.setName("消費(fèi)者2");
consumerThread3.setName("消費(fèi)者3");
producerThread1.setName("生產(chǎn)者甲");
producerThread2.setName("生產(chǎn)者已");
producerThread1.start();
producerThread2.start();
consumerThread1.start();
consumerThread2.start();
consumerThread3.start();
跟傳統(tǒng)的wait和notifyAll方式大同小異
8. 反射中Class.forName()和ClassLoader.loadClass()的區(qū)別
反射中,Class.forName()和ClassLoader的方法loadClass()都可以通過(guò)類名加載類并返回類的Class對(duì)象,但是兩者有一些區(qū)別:
- Class.forName()實(shí)際調(diào)用的是forName0(className, true, ClassLoader.getClassLoader(caller), caller),這個(gè)過(guò)程默認(rèn)將進(jìn)行類加載->鏈接->初始化這一系列過(guò)程
- ClassLoader的方法loadClass()實(shí)際調(diào)用的是ClassLoader.loadClass(className,false)這個(gè)方法,第二個(gè)參數(shù)表示不進(jìn)行鏈接及初始化,僅僅進(jìn)行類加載
- 所有如果加載某個(gè)類需要進(jìn)行鏈接及初始化操作,就需要調(diào)用Class.forName()這個(gè)方法來(lái)獲取Class對(duì)象