Java多線程面試題整理--持續(xù)更新

一 用Runnable還是Thread?

Java中實現(xiàn)多線程有兩種方法:繼承Thread類、實現(xiàn)Runnable接口,在程序開發(fā)中只要是多線程,肯定永遠以實現(xiàn)Runnable接口為主姐帚,因為實現(xiàn)Runnable接口相比繼承Thread類有如下優(yōu)勢:

  1. 可以避免由于Java的單繼承特性而帶來的局限;
  2. 增強程序的健壯性障涯,代碼能夠被多個線程共享罐旗,代碼與數(shù)據(jù)是獨立的膳汪;
  3. 適合多個相同程序代碼的線程區(qū)處理同一資源的情況(資源共享);

二 Thread 類中的start() 和 run() 方法有什么區(qū)別九秀?

  1. start()方法來啟動線程遗嗽,真正實現(xiàn)了多線程運行。調(diào)用start()后颤霎,線程會被放到等待隊列媳谁,等待CPU調(diào)度,并不一定要馬上開始執(zhí)行友酱,只是將這個線程置于就緒狀態(tài)晴音。然后通過JVM,線程Thread會調(diào)用run()方法缔杉,執(zhí)行本線程的線程體锤躁。所以執(zhí)行start()方法后可以繼續(xù)執(zhí)行后面的代碼,不會阻塞或详。
  2. run()方法當作普通方法的方式調(diào)用系羞。程序還是要順序執(zhí)行,要等待run方法體執(zhí)行完畢后霸琴,才可繼續(xù)執(zhí)行下面的代碼椒振; 程序中只有主線程——這一個線程迷帜, 其程序執(zhí)行路徑還是只有一條丐黄, 這樣就沒有達到寫線程的目的。
public class Jyy {
    public static void main(String[] args) {
        Thread thread1 = new Thread(new Runner1());
        Thread thread2 = new Thread(new Runner2());
//        thread1.start();
//        thread2.start();
        thread1.run();
        thread2.run();
    }
}

class Runner1 implements Runnable {
    public void run() {
        System.out.println("進入Runner1運行狀態(tài)");
    }
}

class Runner2 implements Runnable {
    public void run() {
        System.out.println("進入Runner2運行狀態(tài)");
    }
}

執(zhí)行run()方法就像普通方法一樣衅檀,沒有創(chuàng)建新線程选调,總是按順序執(zhí)行:

進入Runner1運行狀態(tài)
進入Runner2運行狀態(tài)

Process finished with exit code 0

執(zhí)行start()方法就會創(chuàng)建新線程夹供,等待CPU進行調(diào)度異步執(zhí)行:

進入Runner2運行狀態(tài)
進入Runner1運行狀態(tài)

Process finished with exit code 0

三 Java中CyclicBarrier 和 CountDownLatch有什么不同?

1 等待多線程完成的CountDownLatch
CountDownLatch與許一個或多個線程等待其他線程完成操作仁堪。例如要解析一個Excel中的多個sheet表哮洽,等到所有sheet表都解析完之后,程序提示解析完成弦聂。

public class Jyy {
    /**
     * parse1先執(zhí)行鸟辅,parse2后執(zhí)行
     * @param args
     */
    public static void main(String[] args) {
        Thread parse1 = new Thread(new Runnable() {
            public void run() {
                System.out.println("parse1");
            }
        });
        
        Thread parse2 = new Thread(new Runnable() {
            public void run() {
                try {
                    parse1.join();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.out.println("parse2");
            }
        });
        
        parse1.start();
        parse2.start();
        
        try {
            parse2.join();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
        System.out.println("finished");
    }
}

程序會按上述邏輯進行,輸出結(jié)果為:

parse1
parse2
finished

join()用于讓當前線程等待調(diào)用join()的線程執(zhí)行結(jié)束横浑。其原理是不停的檢查調(diào)用join()的線程t是否存活剔桨,如果線程t一直存活則當前線程會永遠等待。直到j(luò)oin()線程終止后徙融,線程的notifyAll()會被調(diào)用。join()內(nèi)部實現(xiàn)如下:

if (millis == 0) {
    while (isAlive()) {
        wait(0);
    }
}

CountDownLatch也可以實現(xiàn)join()功能瑰谜。

public class Jyy {
    public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(2);
        
        Thread parse1 = new Thread(new Runnable() {
            public void run() {
                System.out.println("parse1");
                countDownLatch.countDown();
            }
        });
        
        Thread parse2 = new Thread(new Runnable() {
            public void run() {
                System.out.println("parse2");
                countDownLatch.countDown();
            }
        });
        
        parse1.start();
        parse2.start();
        
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
        System.out.println("finished");
    }
}

CountDownLatch的構(gòu)造函數(shù)接收一個int類型的參數(shù)作為計數(shù)器欺冀,如果你想等待N個點完成树绩,這里就傳入N。

當我們調(diào)用一次CountDownLatch的countDown方法時隐轩,N就會減1饺饭,CountDownLatch的await會阻塞當前線程,直到N變成零职车。由于countDown方法可以用在任何地方瘫俊,所以這里說的N個點,可以是N個線程悴灵,也可以是1個線程里的N個執(zhí)行步驟扛芽。用在多個線程時,你只需要把這個CountDownLatch的引用傳遞到線程里积瞒。

注意:計數(shù)器必須大于等于0川尖,只是等于0時候,計數(shù)器就是零茫孔,調(diào)用await方法時不會阻塞當前線程叮喳。CountDownLatch不可能重新初始化或者修改CountDownLatch對象的內(nèi)部計數(shù)器的值。一個線程調(diào)用countDown方法 happen-before 另外一個線程調(diào)用await方法缰贝。

2 同步屏障CyclicBarrier

CyclicBarrier 的字面意思是可循環(huán)使用(Cyclic)的屏障(Barrier)馍悟。它要做的事情是,讓一組線程到達一個屏障(也可以叫同步點)時被阻塞剩晴,直到最后一個線程到達屏障時锣咒,屏障才會開門,所有被屏障攔截的線程才會繼續(xù)干活李破。CyclicBarrier默認的構(gòu)造方法是CyclicBarrier(int parties)宠哄,其參數(shù)表示屏障攔截的線程數(shù)量,每個線程調(diào)用await方法告訴CyclicBarrier我已經(jīng)到達了屏障嗤攻,然后當前線程被阻塞毛嫉。

public class CyclicBarrierTest {
    static CyclicBarrier c = new CyclicBarrier(2);
    public static void main(String[] args) {
        new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    c.await();
                } catch (Exception e) {

                }
                System.out.println(1);
            }
        }).start();

        try {
            c.await();
        } catch (Exception e) {

        }
        System.out.println(2);
    }
}

如果把new CyclicBarrier(2)修改成new CyclicBarrier(3)則主線程和子線程會永遠等待,因為沒有第三個線程執(zhí)行await方法妇菱,即沒有第三個線程到達屏障承粤,所以之前到達屏障的兩個線程都不會繼續(xù)執(zhí)行。

CyclicBarrier還提供一個更高級的構(gòu)造函數(shù)CyclicBarrier(int parties, Runnable barrierAction)闯团,用于在線程到達屏障時辛臊,優(yōu)先執(zhí)行barrierAction,方便處理更復雜的業(yè)務(wù)場景房交。代碼如下:

public class Jyy {
    static CyclicBarrier c = new CyclicBarrier(2, new A());

    public static void main(String[] args) {
        new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    c.await();
                } catch (Exception e) {

                }
                System.out.println(1);
            }
        }).start();

        try {
            c.await();
        } catch (Exception e) {

        }
        System.out.println(2);
    }

    static class A implements Runnable {

        @Override
        public void run() {
            System.out.println(3);
        }

    }
}

輸出結(jié)果:

3 2 1
或 3 1 2

CyclicBarrier的應用場景

CyclicBarrier可以用于多線程計算數(shù)據(jù)彻舰,最后合并計算結(jié)果的應用場景。比如我們用一個Excel保存了用戶所有銀行流水,每個Sheet保存一個帳戶近一年的每筆銀行流水刃唤,現(xiàn)在需要統(tǒng)計用戶的日均銀行流水隔心,先用多線程處理每個sheet里的銀行流水,都執(zhí)行完之后尚胞,得到每個sheet的日均銀行流水硬霍,最后,再用barrierAction用這些線程的計算結(jié)果笼裳,計算出整個Excel的日均銀行流水唯卖。

3 CyclicBarrier和CountDownLatch的區(qū)別

  • CountDownLatch的計數(shù)器只能使用一次。而CyclicBarrier的計數(shù)器可以使用reset() 方法重置躬柬。所以CyclicBarrier能處理更為復雜的業(yè)務(wù)場景拜轨,比如如果計算發(fā)生錯誤,可以重置計數(shù)器楔脯,并讓線程們重新執(zhí)行一次撩轰。
  • CountDownLatch強調(diào)的是一個線程(或多個)需要等待另外的n個線程干完某件事情之后才能繼續(xù)執(zhí)行。 CyclicBarrier強調(diào)的是n個線程昧廷,大家相互等待堪嫂,只要有一個沒完成,所有人都得等著木柬。

一個更加形象的例子參見文章盡量把CyclicBarrier和CountDownLatch的區(qū)別說通俗點

四 一個線程運行時發(fā)生異常會怎樣皆串?

簡單的說,如果異常沒有被捕獲該線程將會停止執(zhí)行眉枕。Thread.UncaughtExceptionHandler是用于處理未捕獲異常造成線程突然中斷情況的一個內(nèi)嵌接口恶复。當一個未捕獲異常將造成線程中斷的時候JVM會使用Thread.getUncaughtExceptionHandler()來查詢線程的UncaughtExceptionHandler并將線程和異常作為參數(shù)傳遞給handler的uncaughtException()方法進行處理。

五 Java中如何停止一個線程速挑?

1 拋出InterruptedException 運行時異常法

調(diào)用線程的 interrupt()方法并檢測中斷狀態(tài)谤牡,拋出一個InterruptedException,捕獲該異常并進行退出邏輯姥宝。

public class Jyy {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        thread.interrupt();
    }
    public static class MyThread extends Thread {
        public void run() {
            try {
                for(int i = 0; i < 50000; i++) {
                    if (this.isInterrupted()) {
                        throw new InterruptedException();
                    }
                    System.out.println(i);
                }
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                System.out.println("exit");
            }
        }
    }
}

2 return法

線程被中斷后翅萤,直接調(diào)用return返回。

public class Jyy {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        thread.interrupt();
    }
    public static class MyThread extends Thread {
        public void run() {
            for(int i = 0; i < 50000; i++) {
                if (this.isInterrupted()) {
                    return;
                }
                System.out.println(i);
            }
        }
    }
}

3 中斷標識位

設(shè)置一個volatile變量作為中斷標識位腊满,當調(diào)用cancel()方法將標識位設(shè)為false時套么,線程結(jié)束。

public class Jyy {
    public static void main(String[] args) {
        MyTask task = new MyTask();
        Thread thread = new Thread(task);
        thread.start();
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        task.cancel();
    }
    public static class MyTask implements Runnable {
        private volatile boolean on = true;
        
        public void run() {
            int i = 0;
            while (on) {
                System.out.println(i++);
            }
        }
        
        public void cancel() {
            on = false;
        }
    }
}

六 Java中notify 和 notifyAll有什么區(qū)別碳蛋?

notify()方法不能喚醒某個具體的線程胚泌,所以只有一個線程在等待的時候它才有用武之地。而notifyAll()喚醒所有線程并允許他們爭奪鎖確保了至少有一個線程能繼續(xù)運行肃弟。

七 為什么wait, notify 和 notifyAll這些方法不在thread類里面玷室?

一個很明顯的原因是JAVA提供的鎖是對象級的而不是線程級的零蓉,每個對象都有鎖,通過線程獲得阵苇。如果線程需要等待某些鎖那么調(diào)用對象中的wait()方法就有意義了壁公。如果wait()方法定義在Thread類中感论,線程正在等待的是哪個鎖就不明顯了绅项。簡單的說,由于wait比肄,notify和notifyAll都是鎖級別的操作快耿,所以把他們定義在Object類中因為鎖屬于對象。

八 Java中interrupted 和 isInterruptedd方法的區(qū)別芳绩?

interrupted()isInterrupted()的主要區(qū)別是前者會將中斷狀態(tài)清除而后者不會掀亥。Java多線程的中斷機制是用內(nèi)部標識來實現(xiàn)的,調(diào)用Thread.interrupt()來中斷一個線程就會設(shè)置中斷標識為true妥色。當中斷線程調(diào)用靜態(tài)方法Thread.interrupted()來檢查中斷狀態(tài)時搪花,中斷狀態(tài)會被清零。

非靜態(tài)方法isInterrupted()用來查詢其它線程的中斷狀態(tài)且不會改變中斷狀態(tài)標識嘹害。 簡單的說就是任何拋出InterruptedException異常的方法都會將中斷狀態(tài)清零撮竿。無論如何,一個線程的中斷狀態(tài)有有可能被其它線程調(diào)用中斷來改變笔呀。

九 什么是線程池幢踏? 為什么要使用它?

參見文章Java線程池解析

十 死鎖

參見文章死鎖詳述

十一 怎么檢測一個線程是否擁有鎖许师?

在java.lang.Thread中有一個方法叫holdsLock()房蝉,它返回true如果當且僅當當前線程擁有某個具體對象的鎖。

十二 Thread類中的yield方法有什么作用微渠?

Yield方法可以暫停當前正在執(zhí)行的線程對象搭幻,讓其它有相同優(yōu)先級的線程執(zhí)行。它是一個靜態(tài)方法而且只保證當前線程放棄CPU占用而不能保證使其它線程一定能占用CPU逞盆,執(zhí)行yield()的線程有可能在進入到暫停狀態(tài)后馬上又被執(zhí)行檀蹋。

十三 單例模式的雙檢鎖是什么?

參見文章雙重檢查機制被破解的聲明

十四 如何在Java中創(chuàng)建線程安全的Singleton纳击?

參見文章 完美的單例模式

十五 線程釋放鎖的時機

  1. 執(zhí)行完同步代碼塊
  2. 在執(zhí)行同步代碼塊的過程中续扔,遇到異常而導致線程終止
  3. 在執(zhí)行同步代碼塊的過程中,執(zhí)行了鎖所屬對象的wait()方法焕数,這個線程會釋放鎖纱昧,進行對象的等待池

十六 什么是上下文切換?

上下文切換(有時也稱做進程切換或任務(wù)切換)是指 CPU 從一個進程或線程切換到另一個進程或線程堡赔。詳情參見文章上下文切換詳解
如何減少上下文切換识脆?

  • 無鎖并發(fā)編程
    多線程競爭鎖時會引起上下文切換,所以多線程處理數(shù)據(jù)時,可以將數(shù)據(jù)的ID利用hash算法取模分段灼捂,不同的線程處理不同的分段
  • CAS
  • 使用最少線程
  • 協(xié)程
    在單線程里實現(xiàn)多任務(wù)的調(diào)度离例,并在單線程中維持多任務(wù)的切換

十七 線程狀態(tài)

線程狀態(tài)

十八 控制并發(fā)線程數(shù)的Semaphore

簡介

Semaphore(信號量)是用來控制同時訪問特定資源的線程數(shù)量,它通過協(xié)調(diào)各個線程悉稠,以保證合理的使用公共資源宫蛆。很多年以來,我都覺得從字面上很難理解Semaphore所表達的含義的猛,只能把它比作是控制流量的紅綠燈耀盗,比如XX馬路要限制流量,只允許同時有一百輛車在這條路上行使卦尊,其他的都必須在路口等待叛拷,所以前一百輛車會看到綠燈,可以開進這條馬路岂却,后面的車會看到紅燈忿薇,不能駛?cè)隭X馬路,但是如果前一百輛中有五輛車已經(jīng)離開了XX馬路躏哩,那么后面就允許有5輛車駛?cè)腭R路署浩,這個例子里說的車就是線程,駛?cè)腭R路就表示線程在執(zhí)行震庭,離開馬路就表示線程執(zhí)行完成瑰抵,看見紅燈就表示線程被阻塞,不能執(zhí)行器联。

應用場景

Semaphore可以用于做流量控制二汛,特別公用資源有限的應用場景,比如數(shù)據(jù)庫連接拨拓。假如有一個需求肴颊,要讀取幾萬個文件的數(shù)據(jù),因為都是IO密集型任務(wù)渣磷,我們可以啟動幾十個線程并發(fā)的讀取婿着,但是如果讀到內(nèi)存后,還需要存儲到數(shù)據(jù)庫中醋界,而數(shù)據(jù)庫的連接數(shù)只有10個竟宋,這時我們必須控制只有十個線程同時獲取數(shù)據(jù)庫連接保存數(shù)據(jù),否則會報錯無法獲取數(shù)據(jù)庫連接形纺。這個時候丘侠,我們就可以使用Semaphore來做流控,代碼如下:

public class SemaphoreTest {

    private static final int THREAD_COUNT = 30;

    private static ExecutorService threadPool = Executors
            .newFixedThreadPool(THREAD_COUNT);

    private static Semaphore s = new Semaphore(10);

    public static void main(String[] args) {
        for (int i = 0; i < THREAD_COUNT; i++) {
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        s.acquire();
                        System.out.println("save data");
                        s.release();
                    } catch (InterruptedException e) {
                    }
                }
            });
        }

        threadPool.shutdown();
    }
}

在代碼中逐样,雖然有30個線程在執(zhí)行蜗字,但是只允許10個并發(fā)的執(zhí)行打肝。Semaphore的構(gòu)造方法Semaphore(int permits) 接受一個整型的數(shù)字,表示可用的許可證數(shù)量挪捕。Semaphore(10)表示允許10個線程獲取許可證粗梭,也就是最大并發(fā)數(shù)是10。Semaphore的用法也很簡單级零,首先線程使用Semaphore的acquire()獲取一個許可證断医,使用完之后調(diào)用release()歸還許可證。還可以用tryAcquire()方法嘗試獲取許可證妄讯。

其他方法

Semaphore還提供一些其他方法:

int availablePermits() :返回此信號量中當前可用的許可證數(shù)孩锡。
int getQueueLength():返回正在等待獲取許可證的線程數(shù)。
boolean hasQueuedThreads() :是否有線程正在等待獲取許可證亥贸。
void reducePermits(int reduction) :減少reduction個許可證。
Collection getQueuedThreads() :返回所有等待獲取許可證的線程集合浇垦。

十九 在單核CPU中炕置,synchronized有意義嗎?

在單核CPU中男韧,多線程是通過競爭CPU時間片并發(fā)執(zhí)行不同線程朴摊,這使得使用者感覺到多線程在并發(fā)執(zhí)行任務(wù)。

Java具有自己的內(nèi)存模型此虑,詳見Java內(nèi)存模型甚纲。線程的共享變量存儲在主存中,線程還有自己的本地內(nèi)存朦前,線程對共享變量進行操作時先要將共享變量存儲在本地內(nèi)存中介杆,之后對本地內(nèi)存中的共享變量副本就行操作,操作完成后將共享變量更新到主存中韭寸。

由此可以看出線程操作共享變量val一共分為三步:

  1. 保存val的副本于本地內(nèi)存
  2. 操作val副本
  3. 更新val到主內(nèi)存

假設(shè)多線程對共享變量val進行操作春哨,線程1在一個CPU時間片內(nèi)完成了1、2步的操恩伺,緊接著線程2獲取了CPU時間片并對val進行操作赴背,由于線程1沒有執(zhí)行第3步所以線程2在第1步讀取到的val仍舊為原值,并沒有感知到線程1對val進行的改變晶渠。

總結(jié):Java內(nèi)存模型是對CPU內(nèi)存模型的一個抽象凰荚,它對于CPU是否是多核不敏感,所以synchronized仍然發(fā)揮其可見性和互斥性語義褒脯。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末便瑟,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子憨颠,更是在濱河造成了極大的恐慌胳徽,老刑警劉巖积锅,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異养盗,居然都是意外死亡缚陷,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進店門往核,熙熙樓的掌柜王于貴愁眉苦臉地迎上來箫爷,“玉大人,你說我怎么就攤上這事聂儒』⒚” “怎么了?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵衩婚,是天一觀的道長窜护。 經(jīng)常有香客問我,道長非春,這世上最難降的妖魔是什么柱徙? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮奇昙,結(jié)果婚禮上护侮,老公的妹妹穿的比我還像新娘。我一直安慰自己储耐,他們只是感情好羊初,可當我...
    茶點故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著什湘,像睡著了一般长赞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上禽炬,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天涧卵,我揣著相機與錄音,去河邊找鬼腹尖。 笑死柳恐,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的热幔。 我是一名探鬼主播乐设,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼绎巨!你這毒婦竟也來了近尚?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤场勤,失蹤者是張志新(化名)和其女友劉穎戈锻,沒想到半個月后歼跟,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡格遭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年哈街,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拒迅。...
    茶點故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡骚秦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出璧微,到底是詐尸還是另有隱情作箍,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布前硫,位于F島的核電站胞得,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏开瞭。R本人自食惡果不足惜懒震,卻給世界環(huán)境...
    茶點故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望嗤详。 院中可真熱鬧,春花似錦瓷炮、人聲如沸葱色。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽苍狰。三九已至,卻和暖如春烘绽,著一層夾襖步出監(jiān)牢的瞬間淋昭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工安接, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留翔忽,地道東北人。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓盏檐,卻偏偏與公主長得像歇式,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子胡野,可洞房花燭夜當晚...
    茶點故事閱讀 43,472評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 下面是我自己收集整理的Java線程相關(guān)的面試題材失,可以用它來好好準備面試。 參考文檔:-《Java核心技術(shù) 卷一》-...
    阿呆變Geek閱讀 14,761評論 14 507
  • 一硫豆、多線程 說明下線程的狀態(tài) java中的線程一共有 5 種狀態(tài)龙巨。 NEW:這種情況指的是笼呆,通過 New 關(guān)鍵字創(chuàng)...
    Java旅行者閱讀 4,662評論 0 44
  • Java SE 基礎(chǔ): 封裝、繼承旨别、多態(tài) 封裝: 概念:就是把對象的屬性和操作(或服務(wù))結(jié)合為一個獨立的整體诗赌,并盡...
    Jayden_Cao閱讀 2,103評論 0 8
  • 不管你是新程序員還是老手,你一定在面試中遇到過有關(guān)線程的問題昼榛。Java語言一個重要的特點就是內(nèi)置了對并發(fā)的支持境肾,讓...
    堯淳閱讀 1,590評論 0 25
  • 在這個百無聊賴的日子里奥喻,按照原定的計劃我應該傍晚七點下班,回家給自己做上一頓“豐盛”的晚餐非迹,酒足飯飽后再慵懶地躺在...
    裝碧里瑅閱讀 566評論 0 2