歷盡數(shù)月,終于整理出一份讓你漲薪8K的Java基礎(chǔ)面試題解析

即將到來金三銀四人才招聘的高峰期,渴望跳槽的朋友肯定跟我一樣四處找以往的面試題,但又感覺找的又不完整,在這里我將把我所見到的題目做一總結(jié),并盡力將答案術(shù)語化、標(biāo)準(zhǔn)化。預(yù)祝大家面試順利。

術(shù)語會(huì)讓你的面試更有說服力,讓你感覺更踏實(shí),建議大家多記背點(diǎn)術(shù)語。

一、簡(jiǎn)單說下什么是跨平臺(tái)

術(shù)語:操作系統(tǒng)指令集、屏蔽系統(tǒng)之間的差異

由于各種操作系統(tǒng)所支持的指令集不是完全一致,所以在操作系統(tǒng)之上加個(gè)虛擬機(jī)可以來提供統(tǒng)一接口,屏蔽系統(tǒng)之間的差異。

二、Java有幾種基本數(shù)據(jù)類型

有八種基本數(shù)據(jù)類型。

各自占用幾字節(jié)也記一下。

三、面向?qū)ο筇卣?/h1>

面向?qū)ο蟮木幊陶Z言有封裝、繼承 、抽象、多態(tài)等4個(gè)主要的特征。

  1. 封裝: 把描述一個(gè)對(duì)象的屬性和行為的代碼封裝在一個(gè)模塊中,也就是一個(gè)類中,屬性用變量定義,行為用方法進(jìn)行定義,方法可以直接訪問同一個(gè)對(duì)象中的屬性。

  2. 抽象: 把現(xiàn)實(shí)生活中的對(duì)象抽象為類。分為過程抽象和數(shù)據(jù)抽象

  • 數(shù)據(jù)抽象 -->鳥有翅膀,羽毛等(類的屬性)
  • 過程抽象 -->鳥會(huì)飛,會(huì)叫(類的方法)
  1. 繼承:子類繼承父類的特征和行為。子類可以有父類的方法,屬性(非private)。子類也可以對(duì)父類進(jìn)行擴(kuò)展,也可以重寫父類的方法。缺點(diǎn)就是提高代碼之間的耦合性。

  2. 多態(tài): 多態(tài)是指程序中定義的引用變量所指向的具體類型和通過該引用變量發(fā)出的方法調(diào)用在編程時(shí)并不確定,而是在程序運(yùn)行期間才確定(比如:向上轉(zhuǎn)型,只有運(yùn)行才能確定其對(duì)象屬性)。方法覆蓋和重載體現(xiàn)了多態(tài)性。

四、為什么要有包裝類型

術(shù)語:讓基本類型也具有對(duì)象的特征

為了讓基本類型也具有對(duì)象的特征,就出現(xiàn)了包裝類型(如我們?cè)谑褂眉项愋虲ollection時(shí)就一定要使用包裝類型而非基本類型)因?yàn)槿萜鞫际茄bobject的,這是就需要這些基本類型的包裝器類了。

自動(dòng)裝箱:new Integer(6);,底層調(diào)用:Integer.valueOf(6)

自動(dòng)拆箱: int i = new Integer(6);,底層調(diào)用i.intValue();方法實(shí)現(xiàn)。

Integer i  = 6;
Integer j = 6;
System.out.println(i==j);

答案在下面這段代碼中找:

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

二者的區(qū)別

  1. 聲明方式不同:基本類型不使用new關(guān)鍵字,而包裝類型需要使用new關(guān)鍵字來在堆中分配存儲(chǔ)空間;
  2. 存儲(chǔ)方式及位置不同:基本類型是直接將變量值存儲(chǔ)在棧中,而包裝類型是將對(duì)象放在堆中,然后通過引用來使用;
  3. 初始值不同:基本類型的初始值如int為0,boolean為false,而包裝類型的初始值為null;
  4. 使用方式不同:基本類型直接賦值直接使用就好,而包裝類型在集合如Collection、Map時(shí)會(huì)使用到。

五、==和equals區(qū)別

  • ==較的是兩個(gè)引用在內(nèi)存中指向的是不是同一對(duì)象(即同一內(nèi)存空間),也就是說在內(nèi)存空間中的存儲(chǔ)位置是否一致。如果兩個(gè)對(duì)象的引用相同時(shí)(指向同一對(duì)象時(shí)),“==”操作符返回true,否則返回flase。
  • equals用來比較某些特征是否一樣。我們平時(shí)用的String類等的equals方法都是重寫后的,實(shí)現(xiàn)比較兩個(gè)對(duì)象的內(nèi)容是否相等。

我們來看看String重寫的equals方法:

它不止判斷了內(nèi)存地址,還增加了字符串是否相同的比較。

public boolean equals(Object anObject) {
    //判斷內(nèi)存地址是否相同
    if (this == anObject) {
        return true;
    }
    // 判斷參數(shù)類型是否是String類型
    if (anObject instanceof String) {
        // 強(qiáng)轉(zhuǎn)
        String anotherString = (String)anObject;
        int n = value.length;
        // 判斷兩個(gè)字符串長(zhǎng)度是否相等
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            // 一一比較 字符是否相同
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

六、String、StringBuffer和StringBuilder區(qū)別

java中String、StringBuffer、StringBuilder是編程中經(jīng)常使用的字符串類,他們之間的區(qū)別也是經(jīng)常在面試中會(huì)問到的問題。現(xiàn)在總結(jié)一下,看看他們的不同與相同。

1. 數(shù)據(jù)可變和不可變

  • String底層使用一個(gè)不可變的字符數(shù)組private final char value[];所以它內(nèi)容不可變。
  • StringBuffer和StringBuilder都繼承了AbstractStringBuilder底層使用的是可變字符數(shù)組:char[] value;

2. 線程安全

  • StringBuilder是線程不安全的,效率較高;而StringBuffer是線程安全的,效率較低。

通過他們的append()方法來看,StringBuffer是有同步鎖,而StringBuilder沒有:

@Override
public synchronized StringBuffer append(Object obj) {
    toStringCache = null;
    super.append(String.valueOf(obj));
    return this;
}
@Override
public StringBuilder append(String str) {
    super.append(str);
    return this;
}

3. 相同點(diǎn)

StringBuilderStringBuffer有公共父類AbstractStringBuilder。

最后,操作可變字符串速度:StringBuilder > StringBuffer > String,這個(gè)答案就顯得不足為奇了。

七、講一下Java中的集合

  • Collection下:List系(有序、元素允許重復(fù))和Set系(無序、元素不重復(fù))

set根據(jù)equals和hashcode判斷,一個(gè)對(duì)象要存儲(chǔ)在Set中,必須重寫equals和hashCode方法

  • Map下:HashMap線程不同步;ConcurrentMap線程同步
  • Collection系列和Map系列:Map是對(duì)Collection的補(bǔ)充,兩個(gè)沒什么關(guān)系

八、ArrayList和LinkedList區(qū)別?

之前專門有寫過ArrayList和LinkedList源碼的文章。

  • ArrayList是實(shí)現(xiàn)了基于動(dòng)態(tài)數(shù)組的數(shù)據(jù)結(jié)構(gòu),LinkedList基于鏈表的數(shù)據(jù)結(jié)構(gòu)。
  • 對(duì)于隨機(jī)訪問get和set,ArrayList覺得優(yōu)于LinkedList,因?yàn)長(zhǎng)inkedList要移動(dòng)指針。
  • 對(duì)于新增和刪除操作add和remove,LinedList比較占優(yōu)勢(shì),因?yàn)锳rrayList要移動(dòng)數(shù)據(jù)。

九、ConcurrentModificationException異常出現(xiàn)的原因

public class Test {
    public static void main(String[] args)  {
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(2);
        Iterator<Integer> iterator = list.iterator();
        while(iterator.hasNext()){
            Integer integer = iterator.next();
            if(integer==2)
                list.remove(integer);
        }
    }
}

執(zhí)行上段代碼是有問題的,會(huì)拋出ConcurrentModificationException異常。

原因:調(diào)用list.remove()方法導(dǎo)致modCount和expectedModCount的值不一致。

final void checkForComodification() {
    if (modCount != expectedModCount)
    throw new ConcurrentModificationException();
}

解決辦法:在迭代器中如果要?jiǎng)h除元素的話,需要調(diào)用Iterator類的remove方法。

public class Test {
    public static void main(String[] args)  {
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(2);
        Iterator<Integer> iterator = list.iterator();
        while(iterator.hasNext()){
            Integer integer = iterator.next();
            if(integer==2)
                iterator.remove();   //注意這個(gè)地方
        }
    }
}

十、HashMap和HashTable、ConcurrentHashMap區(qū)別?

相同點(diǎn)

  • HashMap和Hashtable都實(shí)現(xiàn)了Map接口
  • 都可以存儲(chǔ)key-value數(shù)據(jù)

不同點(diǎn)

  • HashMap可以把null作為key或value,HashTable不可以
  • HashMap線程不安全,效率高。HashTable線程安全,效率低。
  • HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。

什么是fail-fast? 就是最快的時(shí)間能把錯(cuò)誤拋出而不是讓程序執(zhí)行。

1. 如何保證線程安全又效率高?

Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的擴(kuò)展性更好。

ConcurrentHashMap將整個(gè)Map分為N個(gè)segment(類似HashTable),可以提供相同的線程安全,但是效率提升N倍,默認(rèn)N為16。

2. 我們能否讓HashMap同步?

HashMap可以通過下面的語句進(jìn)行同步: Map m = Collections.synchronizeMap(hashMap);

十一、拷貝文件的工具類使用字節(jié)流還是字符流

答案:字節(jié)流

1. 什么是字節(jié)流,什么是字符流?

  • 字節(jié)流:傳遞的是字節(jié)(二進(jìn)制),
  • 字符流:傳遞的是字符

2. 答案

我們并不支持下載的文件有沒有包含字節(jié)流(圖片、影像、音源),所以考慮到通用性,我們會(huì)用字節(jié)流。

十二、線程創(chuàng)建方式

這個(gè)之前自己做過總結(jié),也算比較全面。

1. 方法一:繼承Thread類,作為線程對(duì)象存在(繼承Thread對(duì)象)

public class CreatThreadDemo1 extends Thread{
    /**
     * 構(gòu)造方法: 繼承父類方法的Thread(String name);方法
     * @param name
     */
    public CreatThreadDemo1(String name){
        super(name);
    }

    @Override
    public void run() {
        while (!interrupted()){
            System.out.println(getName()+"線程執(zhí)行了...");
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        CreatThreadDemo1 d1 = new CreatThreadDemo1("first");
        CreatThreadDemo1 d2 = new CreatThreadDemo1("second");

        d1.start();
        d2.start();

        d1.interrupt();  //中斷第一個(gè)線程
    }
}

常規(guī)方法,不多做介紹了,interrupted方法,是來判斷該線程是否被中斷。(終止線程不允許用stop方法,該方法不會(huì)施放占用的資源。所以我們?cè)谠O(shè)計(jì)程序的時(shí)候,要按照中斷線程的思維去設(shè)計(jì),就像上面的代碼一樣)。

讓線程等待的方法

  • Thread.sleep(200); //線程休息2ms
  • Object.wait(); //讓線程進(jìn)入等待,直到調(diào)用Object的notify或者notifyAll時(shí),線程停止休眠

2. 方法二:實(shí)現(xiàn)runnable接口,作為線程任務(wù)存在

public class CreatThreadDemo2 implements Runnable {
    @Override
    public void run() {
        while (true){
            System.out.println("線程執(zhí)行了...");
        }
    }

    public static void main(String[] args) {
        //將線程任務(wù)傳給線程對(duì)象
        Thread thread = new Thread(new CreatThreadDemo2());
        //啟動(dòng)線程
        thread.start();
    }
}

Runnable 只是來修飾線程所執(zhí)行的任務(wù),它不是一個(gè)線程對(duì)象。想要啟動(dòng)Runnable對(duì)象,必須將它放到一個(gè)線程對(duì)象里。

3. 方法三:匿名內(nèi)部類創(chuàng)建線程對(duì)象

public class CreatThreadDemo3 extends Thread{
    public static void main(String[] args) {
        //創(chuàng)建無參線程對(duì)象
        new Thread(){
            @Override
            public void run() {
                System.out.println("線程執(zhí)行了...");
            }
        }.start();
       //創(chuàng)建帶線程任務(wù)的線程對(duì)象
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("線程執(zhí)行了...");
            }
        }).start();
        //創(chuàng)建帶線程任務(wù)并且重寫run方法的線程對(duì)象
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("runnable run 線程執(zhí)行了...");
            }
        }){
            @Override
            public void run() {
                System.out.println("override run 線程執(zhí)行了...");
            }
        }.start();
    }

}

4. 方法四:創(chuàng)建帶返回值的線程

public class CreatThreadDemo4 implements Callable {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CreatThreadDemo4 demo4 = new CreatThreadDemo4();

        FutureTask<Integer> task = new FutureTask<Integer>(demo4); //FutureTask最終實(shí)現(xiàn)的是runnable接口

        Thread thread = new Thread(task);

        thread.start();

        System.out.println("我可以在這里做點(diǎn)別的業(yè)務(wù)邏輯...因?yàn)镕utureTask是提前完成任務(wù)");
        //拿出線程執(zhí)行的返回值
        Integer result = task.get();
        System.out.println("線程中運(yùn)算的結(jié)果為:"+result);
    }

    //重寫Callable接口的call方法
    @Override
    public Object call() throws Exception {
        int result = 1;
        System.out.println("業(yè)務(wù)邏輯計(jì)算中...");
        Thread.sleep(3000);
        return result;
    }
}

Callable接口介紹:

public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

返回指定泛型的call方法。然后調(diào)用FutureTask對(duì)象的get方法得道call方法的返回值。

5. 方法五:定時(shí)器Timer

public class CreatThreadDemo5 {

    public static void main(String[] args) {
        Timer timer = new Timer();

        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("定時(shí)器線程執(zhí)行了...");
            }
        },0,1000);   //延遲0,周期1s

    }
}

6. 方法六:線程池創(chuàng)建線程

public class CreatThreadDemo6 {
    public static void main(String[] args) {
        //創(chuàng)建一個(gè)具有10個(gè)線程的線程池
        ExecutorService threadPool = Executors.newFixedThreadPool(10);
        long threadpoolUseTime = System.currentTimeMillis();
        for (int i = 0;i<10;i++){
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"線程執(zhí)行了...");
                }
            });
        }
        long threadpoolUseTime1 = System.currentTimeMillis();
        System.out.println("多線程用時(shí)"+(threadpoolUseTime1-threadpoolUseTime));
        //銷毀線程池
        threadPool.shutdown();
        threadpoolUseTime = System.currentTimeMillis();
    }

}

7. 方法七:利用java8新特性 stream 實(shí)現(xiàn)并發(fā)

public class CreatThreadDemo7 {
    public static void main(String[] args) {
        List<Integer> values = Arrays.asList(10,20,30,40);
        //parallel 平行的,并行的
        int result = values.parallelStream().mapToInt(p -> p*2).sum();
        System.out.println(result);
        //怎么證明它是并發(fā)處理呢
        values.parallelStream().forEach(p-> System.out.println(p));
    }
}
200
40
10
20
30

怎么證明它是并發(fā)處理呢,他們并不是按照順序輸出的 。

十三、兩個(gè)對(duì)象的hashCode相同,則equals也一定為true,對(duì)嗎?

不對(duì),答案見下面的代碼:

@Override
public int hashCode() {
    return 1;
}

兩個(gè)對(duì)象equals為true,則hashCode也一定相同,對(duì)嗎?

這塊肯定是有爭(zhēng)議的。面試的時(shí)候這樣答:如果按照官方設(shè)計(jì)要求來打代碼的話,hashcode一定相等。但是如果不按官方照設(shè)計(jì)要求、不重寫hashcode方法,就會(huì)出現(xiàn)不相等的情況。

十四、Java線程池用過沒有?

Executors提供了四種方法來創(chuàng)建線程池。

  • newFixedThreadPool() :創(chuàng)建固定大小的線程池。
  • newCachedThreadPool(): 創(chuàng)建無限大小的線程池,線程池中線程數(shù)量不固定,可根據(jù)需求自動(dòng)更改。
  • newSingleThreadPool() : 創(chuàng)建單個(gè)線程池,線程池中只有一個(gè)線程。
  • newScheduledThreadPool() 創(chuàng)建固定大小的線程池,可以延遲或定時(shí)的執(zhí)行任務(wù)。

手寫一個(gè):

public static void main(String[] args) {

    ExecutorService threadPool = Executors.newCachedThreadPool();
    threadPool.execute(() -> {
        for (int i = 0; i< 20;i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    });
    threadPool.shutdown();
}

線程池作用

  • 限制線程個(gè)數(shù),避免線程過多導(dǎo)致系統(tǒng)運(yùn)行緩慢或崩潰。
  • 不需要頻繁的創(chuàng)建和銷毀,節(jié)約資源、響應(yīng)更快。

十五、Math.round(-2.5)等于多少?

不要認(rèn)為它是四舍五入!不要認(rèn)為它是四舍五入!不要認(rèn)為它是四舍五入!

口訣:+0.5后向下取整。所以結(jié)果是-2。

留個(gè)題,Math.round(-2.6)結(jié)果和Math.round(2.6)結(jié)果

十六、面向?qū)ο罅笤瓌t

  1. 單一職責(zé)原則——SRP:讓每個(gè)類只專心處理自己的方法。
  2. 開閉原則——OCP:軟件中的對(duì)象(類,模塊,函數(shù)等)應(yīng)該對(duì)于擴(kuò)展是開放的,但是對(duì)于修改是關(guān)閉的。
  3. 里式替換原則——LSP:子類可以去擴(kuò)展父類,但是不能改變父類原有的功能。
  4. 依賴倒置原則——DIP:應(yīng)該通過調(diào)用接口或抽象類(比較高層),而不是調(diào)用實(shí)現(xiàn)類(細(xì)節(jié))。
  5. 接口隔離原則——ISP:把接口分成滿足依賴關(guān)系的最小接口,實(shí)現(xiàn)類中不能有不需要的方法。
  6. 迪米特原則——LOD:高內(nèi)聚,低耦合。

十七、Static和Final區(qū)別

十八、String s = "hello"和String s = new String("hello");區(qū)別

String s = new String("hello");可能創(chuàng)建兩個(gè)對(duì)象也可能創(chuàng)建一個(gè)對(duì)象。如果常量池中有hello字符串常量的話,則僅僅在堆中創(chuàng)建一個(gè)對(duì)象。如果常量池中沒有hello對(duì)象,則堆上和常量池都需要?jiǎng)?chuàng)建。

String s = "hello"這樣創(chuàng)建的對(duì)象,JVM會(huì)直接檢查字符串常量池是否已有"hello"字符串對(duì)象,如沒有,就分配一個(gè)內(nèi)存存放"hello",如有了,則直接將字符串常量池中的地址返回給棧。(沒有new,沒有堆的操作)

十九、引用類型是占用幾個(gè)字節(jié)?

hotspot在64位平臺(tái)上,占8個(gè)字節(jié),在32位平臺(tái)上占4個(gè)字節(jié)。

二十、(1<3)?"a":"b")+3+4(1<3)?"a":"b")+(3+4)區(qū)別

System.out.println(((1<3)?"a":"b")+3+4);
System.out.println(((1<3)?"a":"b")+(3+4));

控制臺(tái):

a34
a7

1. 什么情況下,加號(hào)會(huì)變成字符串連接符

依據(jù)上面的例子來思考。

二十一、Java中的switch選擇結(jié)構(gòu)可以使用數(shù)據(jù)類型的數(shù)據(jù)(JDK1.8)

  • char
  • byte
  • short
  • int
  • Character
  • Byte
  • Short
  • Integer
  • String
  • enum

更好的記憶方法:

基本類型中,沒有boolean和浮點(diǎn)類型+長(zhǎng)類型long.相應(yīng)的包裝類型也沒有。

外加String和enum。

二十二、4&5``4^5``4&10>>1各等于多少

// 0100 & 0101 = 0100 = 4
System.out.println(4&5);
// 0100 ^ 0101 = 0001 = 1
System.out.println(4^5);
System.out.println(10>>1);
 // 有疑問參考下面的運(yùn)算符優(yōu)先級(jí)
System.out.println(4&10>>1);
4
1
5
4

4|5等于多少呢

答案:5

運(yùn)算符優(yōu)先級(jí)

二十三、某些java類為什么要實(shí)現(xiàn)Serializable接口

為了網(wǎng)絡(luò)進(jìn)行傳輸或者持久化

什么是序列化

將對(duì)象的狀態(tài)信息轉(zhuǎn)換為可以存儲(chǔ)或傳輸?shù)男问降倪^程

除了實(shí)現(xiàn)Serializable接口還有什么序列化方式

  • Json序列化
  • FastJson序列化
  • ProtoBuff序列化 ...

二十四、JVM垃圾處理方法

標(biāo)記-清除算法(老年代)

該算法分為“標(biāo)記”和“清除”兩個(gè)階段: 首先標(biāo)記出所有需要回收的對(duì)象(可達(dá)性分析), 在標(biāo)記完成后統(tǒng)一清理掉所有被標(biāo)記的對(duì)象.

該算法會(huì)有兩個(gè)問題:

  • 效率問題:標(biāo)記和清除效率不高。
  • 空間問題:標(biāo)記清除后會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片, 空間碎片太多可能會(huì)導(dǎo)致在運(yùn)行過程中需要分配較大對(duì)象時(shí)無法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)另一次垃圾收集。

所以它一般用于"垃圾不太多的區(qū)域,比如老年代"。

復(fù)制算法(新生代)

該算法的核心是將可用內(nèi)存按容量劃分為大小相等的兩塊, 每次只用其中一塊, 當(dāng)這一塊的內(nèi)存用完, 就將還存活的對(duì)象(非垃圾)復(fù)制到另外一塊上面, 然后把已使用過的內(nèi)存空間一次清理掉.

優(yōu)點(diǎn):不用考慮碎片問題,方法簡(jiǎn)單高效。 缺點(diǎn):內(nèi)存浪費(fèi)嚴(yán)重。

現(xiàn)代商用VM的新生代均采用復(fù)制算法, 但由于新生代中的98%的對(duì)象都是生存周期極短的, 因此并不需完全按照1∶1的比例劃分新生代空間, 而是將新生代劃分為一塊較大的Eden區(qū)和兩塊較小的Survivor區(qū)(HotSpot默認(rèn)Eden和Survivor的大小比例為8∶1), 每次只用Eden和其中一塊Survivor. 當(dāng)發(fā)生MinorGC時(shí), 將Eden和Survivor中還存活著的對(duì)象一次性地拷貝到另外一塊Survivor上, 最后清理掉Eden和剛才用過的Survivor的空間. 當(dāng)Survivor空間不夠用(不足以保存尚存活的對(duì)象)時(shí), 需要依賴?yán)夏甏M(jìn)行空間分配擔(dān)保機(jī)制, 這部分內(nèi)存直接進(jìn)入老年代。

復(fù)制算法的空間分配擔(dān)保: 在執(zhí)行Minor GC前, VM會(huì)首先檢查老年代是否有足夠的空間存放新生代尚存活對(duì)象, 由于新生代使用復(fù)制收集算法, 為了提升內(nèi)存利用率, 只使用了其中一個(gè)Survivor作為輪換備份, 因此當(dāng)出現(xiàn)大量對(duì)象在Minor GC后仍然存活的情況時(shí), 就需要老年代進(jìn)行分配擔(dān)保, 讓Survivor無法容納的對(duì)象直接進(jìn)入老年代, 但前提是老年代需要有足夠的空間容納這些存活對(duì)象. 但存活對(duì)象的大小在實(shí)際完成GC前是無法明確知道的, 因此Minor GC前, VM會(huì)先首先檢查老年代連續(xù)空間是否大于新生代對(duì)象總大小或歷次晉升的平均大小, 如果條件成立, 則進(jìn)行Minor GC, 否則進(jìn)行Full GC(讓老年代騰出更多空間). 然而取歷次晉升的對(duì)象的平均大小也是有一定風(fēng)險(xiǎn)的, 如果某次Minor GC存活后的對(duì)象突增,遠(yuǎn)遠(yuǎn)高于平均值的話,依然可能導(dǎo)致?lián)J?Handle Promotion Failure, 老年代也無法存放這些對(duì)象了), 此時(shí)就只好在失敗后重新發(fā)起一次Full GC(讓老年代騰出更多空間).

標(biāo)記-整理算法(老年代)

標(biāo)記清除算法會(huì)產(chǎn)生內(nèi)存碎片問題, 而復(fù)制算法需要有額外的內(nèi)存擔(dān)保空間, 于是針對(duì)老年代的特點(diǎn), 又有了標(biāo)記整理算法. 標(biāo)記整理算法的標(biāo)記過程與標(biāo)記清除算法相同, 但后續(xù)步驟不再對(duì)可回收對(duì)象直接清理, 而是讓所有存活的對(duì)象都向一端移動(dòng),然后清理掉端邊界以外的內(nèi)存.

二十五、新生代、老年代、持久代都存儲(chǔ)哪些東西

新生代

  • 方法中new一個(gè)對(duì)象,就會(huì)先進(jìn)入新生代。

老年代

  • 新生代中經(jīng)歷了N次垃圾回收仍然存活的對(duì)象就會(huì)被放到老年代中。
  • 大對(duì)象一般直接放入老年代。
  • 當(dāng)Survivor空間不足。需要老年代擔(dān)保一些空間,也會(huì)將對(duì)象放入老年代。

永久代

  • 指的就是方法區(qū)。

二十六、可達(dá)性算法中,哪些對(duì)象可作為GC Roots對(duì)象。

  • 虛擬機(jī)棧中引用的對(duì)象
  • 方法區(qū)靜態(tài)成員引用的對(duì)象
  • 方法區(qū)常量引用對(duì)象
  • 本地方法棧JNI引用的對(duì)象

二十七、什么時(shí)候進(jìn)行MinGC和FullGC

MinGC

  • 當(dāng)Eden區(qū)滿時(shí),觸發(fā)Minor GC.

FullGC

  • 調(diào)用System.gc時(shí),系統(tǒng)建議執(zhí)行Full GC,但是不必然執(zhí)行
  • 老年代空間不足
  • 方法區(qū)空間不足
  • 通過Minor GC后進(jìn)入老年代的平均大小大于老年代的剩余空間
  • 堆中分配很大的對(duì)象,而老年代沒有足夠的空間

二十八、如何判定對(duì)象為垃圾對(duì)象

在堆里面存放著Java世界中幾乎所有的對(duì)象實(shí)例, 垃圾收集器在對(duì)堆進(jìn)行回收前, 第一件事就是判斷哪些對(duì)象已死(可回收).

引用計(jì)數(shù)法

在JDK1.2之前,使用的是引用計(jì)數(shù)器算法。 在對(duì)象中添加一個(gè)引用計(jì)數(shù)器,當(dāng)有地方引用這個(gè)對(duì)象的時(shí)候,引用計(jì)數(shù)器的值就+1,當(dāng)引用失效的時(shí)候,計(jì)數(shù)器的值就-1,當(dāng)引用計(jì)數(shù)器被減為零的時(shí)候,標(biāo)志著這個(gè)對(duì)象已經(jīng)沒有引用了,可以回收了!

問題:如果在A類中調(diào)用B類的方法,B類中調(diào)用A類的方法,這樣當(dāng)其他所有的引用都消失了之后,A和B還有一個(gè)相互的引用,也就是說兩個(gè)對(duì)象的引用計(jì)數(shù)器各為1,而實(shí)際上這兩個(gè)對(duì)象都已經(jīng)沒有額外的引用,已經(jīng)是垃圾了。但是該算法并不會(huì)計(jì)算出該類型的垃圾。

可達(dá)性分析法

在主流商用語言(如Java、C#)的主流實(shí)現(xiàn)中, 都是通過可達(dá)性分析算法來判定對(duì)象是否存活的: 通過一系列的稱為 GC Roots 的對(duì)象作為起點(diǎn), 然后向下搜索; 搜索所走過的路徑稱為引用鏈/Reference Chain, 當(dāng)一個(gè)對(duì)象到 GC Roots 沒有任何引用鏈相連時(shí), 即該對(duì)象不可達(dá), 也就說明此對(duì)象是不可用的, 如下圖:雖然E和F相互關(guān)聯(lián), 但它們到GC Roots是不可達(dá)的, 因此也會(huì)被判定為可回收的對(duì)象。

: 即使在可達(dá)性分析算法中不可達(dá)的對(duì)象, VM也并不是馬上對(duì)其回收, 因?yàn)橐嬲嬉粋€(gè)對(duì)象死亡, 至少要經(jīng)歷兩次標(biāo)記過程: 第一次是在可達(dá)性分析后發(fā)現(xiàn)沒有與GC Roots相連接的引用鏈, 第二次是GC對(duì)在F-Queue執(zhí)行隊(duì)列中的對(duì)象進(jìn)行的小規(guī)模標(biāo)記(對(duì)象需要覆蓋finalize()方法且沒被調(diào)用過).

二十九、你能說出來幾個(gè)垃圾收集器

1. Serial

Serial收集器是Hotspot運(yùn)行在Client模式下的默認(rèn)新生代收集器, 它在進(jìn)行垃圾收集時(shí),會(huì)暫停所有的工作進(jìn)程,用一個(gè)線程去完成GC工作

特點(diǎn):簡(jiǎn)單高效,適合jvm管理內(nèi)存不大的情況(十兆到百兆)。

2. Parnew

ParNew收集器其實(shí)是Serial的多線程版本,回收策略完全一樣,但是他們又有著不同。

我們說了Parnew是多線程gc收集,所以它配合多核心的cpu效果更好,如果是一個(gè)cpu,他倆效果就差不多。(可用-XX:ParallelGCThreads參數(shù)控制GC線程數(shù))

3. Cms

CMS(Concurrent Mark Sweep)收集器是一款具有劃時(shí)代意義的收集器, 一款真正意義上的并發(fā)收集器, 雖然現(xiàn)在已經(jīng)有了理論意義上表現(xiàn)更好的G1收集器, 但現(xiàn)在主流互聯(lián)網(wǎng)企業(yè)線上選用的仍是CMS(如Taobao),又稱多并發(fā)低暫停的收集器。

由他的英文組成可以看出,它是基于標(biāo)記-清除算法實(shí)現(xiàn)的。整個(gè)過程分4個(gè)步驟:

  • 初始標(biāo)記(CMS initial mark):僅只標(biāo)記一下GC Roots能直接關(guān)聯(lián)到的對(duì)象, 速度很快
  • 并發(fā)標(biāo)記(CMS concurrent mark: GC Roots Tracing過程)
  • 重新標(biāo)記(CMS remark):修正并發(fā)標(biāo)記期間因用戶程序繼續(xù)運(yùn)行而導(dǎo)致標(biāo)記產(chǎn)生變動(dòng)的那一部分對(duì)象的標(biāo)記記錄
  • 并發(fā)清除(CMS concurrent sweep: 已死對(duì)象將會(huì)就地釋放)

可以看到,初始標(biāo)記、重新標(biāo)記需要STW(stop the world 即:掛起用戶線程)操作。因?yàn)樽詈臅r(shí)的操作是并發(fā)標(biāo)記和并發(fā)清除。所以總體上我們認(rèn)為CMS的GC與用戶線程是并發(fā)運(yùn)行的。

優(yōu)點(diǎn):并發(fā)收集、低停頓

缺點(diǎn)

  • CMS默認(rèn)啟動(dòng)的回收線程數(shù)=(CPU數(shù)目+3)*4 當(dāng)CPU數(shù)>4時(shí), GC線程最多占用不超過25%的CPU資源, 但是當(dāng)CPU數(shù)<=4時(shí), GC線程可能就會(huì)過多的占用用戶CPU資源, 從而導(dǎo)致應(yīng)用程序變慢, 總吞吐量降低.
  • 無法清除浮動(dòng)垃圾(GC運(yùn)行到并發(fā)清除階段時(shí)用戶線程產(chǎn)生的垃圾),因?yàn)橛脩艟€程是需要內(nèi)存的,如果浮動(dòng)垃圾施放不及時(shí),很可能就造成內(nèi)存溢出,所以CMS不能像別的垃圾收集器那樣等老年代幾乎滿了才觸發(fā),CMS提供了參數(shù)-XX:CMSInitiatingOccupancyFraction來設(shè)置GC觸發(fā)百分比(1.6后默認(rèn)92%),當(dāng)然我們還得設(shè)置啟用該策略-XX:+UseCMSInitiatingOccupancyOnly
  • 因?yàn)镃MS采用標(biāo)記-清除算法,所以可能會(huì)帶來很多的碎片,如果碎片太多沒有清理,jvm會(huì)因?yàn)闊o法分配大對(duì)象內(nèi)存而觸發(fā)GC,因此CMS提供了-XX:+UseCMSCompactAtFullCollection參數(shù),它會(huì)在GC執(zhí)行完后接著進(jìn)行碎片整理,但是又會(huì)有個(gè)問題,碎片整理不能并發(fā),所以必須單線程去處理,所以如果每次GC完都整理用戶線程stop的時(shí)間累積會(huì)很長(zhǎng),所以XX:CMSFullGCsBeforeCompaction參數(shù)設(shè)置隔幾次GC進(jìn)行一次碎片整理(默認(rèn)為0)。

4. G1

同優(yōu)秀的CMS垃圾回收器一樣,G1也是關(guān)注最小時(shí)延的垃圾回收器,也同樣適合大尺寸堆內(nèi)存的垃圾收集,官方也推薦使用G1來代替選擇CMS。G1最大的特點(diǎn)是引入分區(qū)的思路,弱化分代的概念,合理利用垃圾收集各個(gè)周期的資源,解決了其他收集器甚至CMS的眾多缺陷。

因?yàn)槊總€(gè)區(qū)都有E、S、O代,所以在G1中,不需要對(duì)整個(gè)Eden等代進(jìn)行回收,而是尋找可回收對(duì)象比較多的區(qū),然后進(jìn)行回收(雖然也需要STW操作,但是花費(fèi)的時(shí)間是很少的),保證高效率。

新生代收集

G1的新生代收集跟ParNew類似,如果存活時(shí)間超過某個(gè)閾值,就會(huì)被轉(zhuǎn)移到S/O區(qū)。

年輕代內(nèi)存由一組不連續(xù)的heap區(qū)組成, 這種方法使得可以動(dòng)態(tài)調(diào)整各代區(qū)域的大小

老年代收集

分為以下幾個(gè)階段:

  • 初始標(biāo)記 (Initial Mark: Stop the World Event) 在G1中, 該操作附著一次年輕代GC, 以標(biāo)記Survivor中有可能引用到老年代對(duì)象的Regions.
  • 掃描根區(qū)域 (Root Region Scanning: 與應(yīng)用程序并發(fā)執(zhí)行) 掃描Survivor中能夠引用到老年代的references. 但必須在Minor GC觸發(fā)前執(zhí)行完
  • 并發(fā)標(biāo)記 (Concurrent Marking : 與應(yīng)用程序并發(fā)執(zhí)行) 在整個(gè)堆中查找存活對(duì)象, 但該階段可能會(huì)被Minor GC中斷
  • 重新標(biāo)記 (Remark : Stop the World Event) 完成堆內(nèi)存中存活對(duì)象的標(biāo)記. 使用snapshot-at-the-beginning(SATB, 起始快照)算法, 比CMS所用算法要快得多(空Region直接被移除并回收, 并計(jì)算所有區(qū)域的活躍度).
  • 清理 (Cleanup : Stop the World Event and Concurrent) 在含有存活對(duì)象和完全空閑的區(qū)域上進(jìn)行統(tǒng)計(jì)(STW)、擦除Remembered Sets(使用Remembered Set來避免掃描全堆,每個(gè)區(qū)都有對(duì)應(yīng)一個(gè)Set用來記錄引用信息、讀寫操作記錄)(STW)、重置空regions并將他們返還給空閑列表(free list)(Concurrent)

三十、JVM中對(duì)象的創(chuàng)建過程

1. 拿到內(nèi)存創(chuàng)建指令

當(dāng)虛擬機(jī)遇到內(nèi)存創(chuàng)建的指令的時(shí)候(new 類名),來到了方法區(qū),找 根據(jù)new的參數(shù)在常量池中定位一個(gè)類的符號(hào)引用。

2. 檢查符號(hào)引用

檢查該符號(hào)引用有沒有被加載、解析和初始化過,如果沒有則執(zhí)行類加載過程,否則直接準(zhǔn)備為新的對(duì)象分配內(nèi)存

3. 分配內(nèi)存

虛擬機(jī)為對(duì)象分配內(nèi)存(堆)分配內(nèi)存分為指針碰撞和空閑列表兩種方式;分配內(nèi)存還要要保證并發(fā)安全,有兩種方式。

3.1. 指針碰撞

所有的存儲(chǔ)空間分為兩部分,一部分是空閑,一部分是占用,需要分配空間的時(shí)候,只需要計(jì)算指針移動(dòng)的長(zhǎng)度即可。

3.2. 空閑列表

虛擬機(jī)維護(hù)了一個(gè)空閑列表,需要分配空間的時(shí)候去查該空閑列表進(jìn)行分配并對(duì)空閑列表做更新。

可以看出,內(nèi)存分配方式是由java堆是否規(guī)整決定的,java堆的規(guī)整是由垃圾回收機(jī)制來決定的

3.2.1 安全性問題的思考

假如分配內(nèi)存策略是指針碰撞,如果在高并發(fā)情況下,多個(gè)對(duì)象需要分配內(nèi)存,如果不做處理,肯定會(huì)出現(xiàn)線程安全問題,導(dǎo)致一些對(duì)象分配不到空間等。

3.3 線程同步策略

也就是每個(gè)線程都進(jìn)行同步,防止出現(xiàn)線程安全。

3.4. 本地線程分配緩沖

也稱TLAB(Thread Local Allocation Buffer),在堆中為每一個(gè)線程分配一小塊獨(dú)立的內(nèi)存,這樣以來就不存并發(fā)問題了,Java 層面與之對(duì)應(yīng)的是 ThreadLocal 類的實(shí)現(xiàn)

4. 初始化

分配完內(nèi)存后要對(duì)對(duì)象的頭(Object Header)進(jìn)行初始化,這新信息包括:該對(duì)象對(duì)應(yīng)類的元數(shù)據(jù)、該對(duì)象的GC代、對(duì)象的哈希碼。
抽象數(shù)據(jù)類型默認(rèn)初始化為null,基本數(shù)據(jù)類型為0,布爾為false。。。

5. 調(diào)用對(duì)象的初始化方法

也就是執(zhí)行構(gòu)造方法。

三十一、談?wù)剬?duì)象的訪問定位

對(duì)象創(chuàng)建起來之后,就會(huì)在虛擬機(jī)棧中維護(hù)一個(gè)本地變量表,用于存儲(chǔ)基礎(chǔ)類型和基礎(chǔ)類型的值,引用類型與引用類型的值。 其中引用類型的值就是堆中對(duì)象地址。如何引用堆中地址有兩種方式:

  • 句柄:在堆中維護(hù)一個(gè)句柄池,句柄中包含了對(duì)象地址,當(dāng)對(duì)象改變的時(shí)候,只需改變句柄,不需要改變棧中本地變量表的引用
  • 直接指針:對(duì)象的地址直接存儲(chǔ)在棧中,這樣做的好處就是訪問速度變快(Hotspot采用該方式)

三十二、JVM將內(nèi)存主要?jiǎng)澐譃槟奈宀糠?/h1>

方法區(qū)、虛擬機(jī)棧、本地方法棧、堆、程序計(jì)數(shù)器。

三十三、String的intern()函數(shù)作用

這個(gè)要分版本來回答:

  • 如果是JDK6,如果字符串產(chǎn)量池先前已經(jīng)創(chuàng)建該對(duì)象,則返回引用;否則將其添加到字符串常量池并返回引用。
  • 如果是JDK6+,若字符串常量池有則返回引用,如果池中沒有堆中有,則將堆中的引用添加到池中(注意是引用),然后返回引用;若池中也沒有,則在池中創(chuàng)建并返回引用。

三十四、本地方法棧和虛擬機(jī)棧區(qū)別

本地方法棧與虛擬機(jī)棧所發(fā)揮的作用很相似,他們的區(qū)別在于虛擬機(jī)棧為執(zhí)行Java代碼方法服務(wù),而本地方法棧是為Native方法服務(wù)。

三十五、分配堆內(nèi)存指令

-Xms -Xmx

前者是堆的初始值,后者是堆能達(dá)到的最大值。

三十六、程序計(jì)數(shù)器作用

記錄當(dāng)前線程鎖執(zhí)行的字節(jié)碼的行號(hào)。

  • 程序計(jì)數(shù)器是一塊較小的內(nèi)存空間。
  • 處于線程獨(dú)占區(qū)。
  • 執(zhí)行java方法時(shí),它記錄正在執(zhí)行的虛擬機(jī)字節(jié)碼指令地址。執(zhí)行native方法,它的值為undefined
  • 該區(qū)域是唯一一個(gè)沒有規(guī)定任何OutOfMemoryError的區(qū)域

三十七、如何將字符串反轉(zhuǎn)?

  • 通過 charAt(int index)返回char值進(jìn)行字符串拼接
  • 調(diào)用StringBuffer中的reverse方法

三十八、Collection 和 Collections 有什么區(qū)別?

Collection 是一個(gè)集合接口。它提供了對(duì)集合對(duì)象進(jìn)行基本操作的通用接口方法。Collection接口在Java 類庫中有很多具體的實(shí)現(xiàn)。Collection接口的意義是為各種具體的集合提供了最大化的統(tǒng)一操作方式。

Collections 是一個(gè)包裝類。它包含有各種有關(guān)集合操作的靜態(tài)多態(tài)方法。此類不能實(shí)例化,就像一個(gè)工具類,服務(wù)于Java的Collection框架。

三十九、在 Queue 中 poll()和 remove()有什么區(qū)別?

  • queue的增加元素方法add和offer的區(qū)別在于,add方法在隊(duì)列滿的情況下將選擇拋異常的方法來表示隊(duì)列已經(jīng)滿了,而offer方法通過返回false表示隊(duì)列已經(jīng)滿了;在有限隊(duì)列的情況,使用offer方法優(yōu)于add方法;
  • remove方法和poll方法都是刪除隊(duì)列的頭元素,remove方法在隊(duì)列為空的情況下將拋異常,而poll方法將返回null;
  • element和peek方法都是返回隊(duì)列的頭元素,但是不刪除頭元素,區(qū)別在與element方法在隊(duì)列為空的情況下,將拋異常,而peek方法將返回null.

四十、什么是迭代器

Iterator接口提供了很多對(duì)集合元素進(jìn)行迭代的方法。每一個(gè)集合類都包括了可以返回迭代器實(shí)例的迭代方法。迭代器可以在迭代過程中刪除底層集合的元素,但是不可以直接調(diào)用集合的remove(Object obj)刪除,可以通過迭代器的remove()方法刪除

四十一、迭代器的優(yōu)點(diǎn)

如果用的是for循環(huán),就用集合自帶的remove(),而這樣就改變了集合的Size()循環(huán)的時(shí)候會(huì)出錯(cuò)。但如果把集合放入迭代器,既iterator迭代可以遍歷并選擇集合中的每個(gè)對(duì)象而不改變集合的結(jié)構(gòu),而把集合放入迭代器,用迭代器的remove()就不會(huì)出現(xiàn)問題

四十二、Java集合類中的Iterator和ListIterator的區(qū)別

對(duì)List來說,你也可以通過listIterator()取得其迭代器,兩種迭代器在有些時(shí)候是不能通用的,Iterator和ListIterator主要區(qū)別在以下方面:

  • iterator()方法在set和list接口中都有定義,但是ListIterator()僅存在于list接口中(或?qū)崿F(xiàn)類中);
  • ListIterator有add()方法,可以向List中添加對(duì)象,而Iterator不能
  • ListIterator和Iterator都有hasNext()和next()方法,可以實(shí)現(xiàn)順序向后遍歷,但是ListIterator有hasPrevious()和previous()方法,可以實(shí)現(xiàn)逆向(順序向前)遍歷。Iterator就不可以。
  • ListIterator可以定位當(dāng)前的索引位置,nextIndex()和previousIndex()可以實(shí)現(xiàn)。Iterator沒有此功能。
  • 都可實(shí)現(xiàn)刪除對(duì)象,但是ListIterator可以實(shí)現(xiàn)對(duì)象的修改,set()方法可以實(shí)現(xiàn)。Iierator僅能遍歷,不能修改。

四十三、怎么確保一個(gè)集合不能被修改?

  • Java中提供final關(guān)鍵字,對(duì)基本類型進(jìn)行修飾,當(dāng)?shù)谝淮纬跏蓟?,該變量就不可被修?/li>
  • Collections工具類中的UnmodifiableList(不可修改的List、Map、Set等)

四十四、并行和并發(fā)區(qū)別

并發(fā)的關(guān)鍵是你有處理多個(gè)任務(wù)的能力,不一定要同時(shí)。

四十五、說一下你對(duì)Daemon線程(守護(hù)線程)的理解?它有什么意義?一般應(yīng)用于什么樣的場(chǎng)景?

所謂守護(hù)線程是指在程序運(yùn)行的時(shí)候在后臺(tái)提供一種通用服務(wù)的線程,比如垃圾回收線程就是一個(gè)很稱職的守護(hù)者,并且這種線程并不屬于程序中不可或缺的部分。因 此,當(dāng)所有的非守護(hù)線程結(jié)束時(shí),程序也就終止了,同時(shí)會(huì)殺死進(jìn)程中的所有守護(hù)線程。反過來說,只要任何非守護(hù)線程還在運(yùn)行,程序就不會(huì)終止。

守護(hù)線程和用戶線程的沒啥本質(zhì)的區(qū)別:唯一的不同之處就在于虛擬機(jī)的離開:如果用戶線程已經(jīng)全部退出運(yùn)行了,只剩下守護(hù)線程存在了,虛擬機(jī)也就退出了。 因?yàn)闆]有了被守護(hù)者,守護(hù)線程也就沒有工作可做了,也就沒有繼續(xù)運(yùn)行程序的必要了。

四十六、sleep() 和 wait() 有什么區(qū)別?

對(duì)于sleep()方法,我們首先要知道該方法是屬于Thread類中的。而wait()方法,則是屬于Object類中的。sleep()方法導(dǎo)致了程序暫停執(zhí)行指定的時(shí)間,讓出cpu該其他線程,但是他的監(jiān)控狀態(tài)依然保持者,當(dāng)指定的時(shí)間到了又會(huì)自動(dòng)恢復(fù)運(yùn)行狀態(tài)。在調(diào)用sleep()方法的過程中,線程不會(huì)釋放對(duì)象鎖。

而當(dāng)調(diào)用wait()方法的時(shí)候,線程會(huì)放棄對(duì)象鎖,進(jìn)入等待此對(duì)象的等待鎖定池,只有針對(duì)此對(duì)象調(diào)用notify()方法后本線程才進(jìn)入對(duì)象鎖定池準(zhǔn)備獲取對(duì)象鎖進(jìn)入運(yùn)行狀態(tài)。

四十七、notify 和 notifyAll 區(qū)別

notify 僅僅通知一個(gè)線程,并且我們不知道哪個(gè)線程會(huì)收到通知,然而 notifyAll 會(huì)通知所有等待中的線程。換言之,如果只有一個(gè)線程在等待一個(gè)信號(hào)燈,notify和notifyAll都會(huì)通知到這個(gè)線程。但如果多個(gè)線程在等待這個(gè)信號(hào)燈,那么notify只會(huì)通知到其中一個(gè),而其它線程并不會(huì)收到任何通知,而notifyAll會(huì)喚醒所有等待中的線程。

四十八、線程中start()和run()的區(qū)別

  • 每個(gè)線程都有要執(zhí)行的任務(wù)。線程的任務(wù)處理邏輯可以在Tread類的run實(shí)例方法中直接實(shí)現(xiàn)或通過該方法進(jìn)行調(diào)用,因此run()相當(dāng)于線程的任務(wù)處理邏輯的入口方法,它由Java虛擬機(jī)在運(yùn)行相應(yīng)線程時(shí)直接調(diào)用,而不是由應(yīng)用代碼進(jìn)行調(diào)用。
  • 而start()的作用是啟動(dòng)相應(yīng)的線程。啟動(dòng)一個(gè)線程實(shí)際是請(qǐng)求Java虛擬機(jī)運(yùn)行相應(yīng)的線程,而這個(gè)線程何時(shí)能夠運(yùn)行是由線程調(diào)度器決定的。start()調(diào)用結(jié)束并不表示相應(yīng)線程已經(jīng)開始運(yùn)行,這個(gè)線程可能稍后運(yùn)行,也可能永遠(yuǎn)也不會(huì)運(yùn)行。

四十九、線程池的五種狀態(tài)

線程池的5種狀態(tài):Running、ShutDown、Stop、Tidying、Terminated。

五十、線程池中 submit()和 execute()方法有什么區(qū)別?

  • execute(Runnable x) 沒有返回值??梢詧?zhí)行任務(wù),但無法判斷任務(wù)是否成功完成。——實(shí)現(xiàn)Runnable接口
  • submit(Runnable x) 返回一個(gè)future??梢杂眠@個(gè)future來判斷任務(wù)是否成功完成?!獙?shí)現(xiàn)Callable接口

# 鏈接 Java程序員福利"常用資料分享"

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 《深入理解Java虛擬機(jī)》筆記_第一遍 先取看完這本書(JVM)后必須掌握的部分。 第一部分 走近 Java 從傳...
    xiaogmail閱讀 5,474評(píng)論 1 34
  • JVM架構(gòu) 當(dāng)一個(gè)程序啟動(dòng)之前,它的class會(huì)被類裝載器裝入方法區(qū)(Permanent區(qū)),執(zhí)行引擎讀取方法區(qū)的...
    cocohaifang閱讀 1,845評(píng)論 0 7
  • 介紹JVM中7個(gè)區(qū)域,然后把每個(gè)區(qū)域可能造成內(nèi)存的溢出的情況說明 程序計(jì)數(shù)器:看做當(dāng)前線程所執(zhí)行的字節(jié)碼行號(hào)指示器...
    jemmm閱讀 2,306評(píng)論 0 9
  • HostSpot虛擬機(jī)運(yùn)行時(shí)內(nèi)存 程序計(jì)數(shù)器——當(dāng)前線程執(zhí)行字節(jié)碼的行號(hào)指示器,如果執(zhí)行Native方法,則計(jì)數(shù)器...
    Mars_M閱讀 942評(píng)論 0 3
  • Java和C++之間有一堵由內(nèi)存動(dòng)態(tài)分配和垃圾收集技術(shù)所圍成的“高墻”,墻外面的人想進(jìn)來,墻里面的人想出來。 對(duì)象...
    胡二囧閱讀 1,330評(píng)論 0 4

友情鏈接更多精彩內(nèi)容