Java線程同步的幾種方式

本文主要是介紹java中線程同步的幾種常用方式击你。

CountDownLatch

從字面上理解剃氧,CountDownLatch是一個(gè)同步等待的鎖敏储,根據(jù)官方的注釋可以看出這其實(shí)是一個(gè)同步執(zhí)行工具類。

先看一下官方注釋的前兩段

/**
 * A synchronization aid that allows one or more threads to wait until
 * a set of operations being performed in other threads completes.
 *
 * <p>A {@code CountDownLatch} is initialized with a given <em>count</em>.
 * The {@link #await await} methods block until the current count reaches
 * zero due to invocations of the {@link #countDown} method, after which
 * all waiting threads are released and any subsequent invocations of
 * {@link #await await} return immediately.  This is a one-shot phenomenon
 * -- the count cannot be reset.  If you need a version that resets the
 * count, consider using a {@link CyclicBarrier}.
 

翻譯一下就是:

 /**
  * CountDownLatch是一個(gè)朋鞍,允許一個(gè)或多個(gè)線程已添,
  * 等待其他線程中執(zhí)行的一組操作完成的,同步輔助工具滥酥。
  * 
  * CountDownLatch用給定的計(jì)數(shù)進(jìn)行初始化更舞。 
  * 當(dāng)線程點(diǎn)用await方法后被阻塞,直到當(dāng)前計(jì)數(shù)由于其他線程調(diào)用countDown()方法而達(dá)到零坎吻,
  * 此后所有等待線程被放缆蝉,并且任何后續(xù)調(diào)用await立即返回。
  * 這是一次性的操作瘦真,計(jì)數(shù)無(wú)法重置刊头。
  * 如果您需要重置計(jì)數(shù)的版本,請(qǐng)考慮使用CyclicBarrier诸尽。
  * /

解釋的很清楚原杂,不在贅述,接著看一下官方提供的偽代碼案例

官方案例一

class Driver { // ...
   void main() throws InterruptedException {
     CountDownLatch startSignal = new CountDownLatch(1);
     CountDownLatch doneSignal = new CountDownLatch(N);

     for (int i = 0; i < N; ++i) // create and start threads
       new Thread(new Worker(startSignal, doneSignal)).start();

     doSomethingElse();            // don't let run yet
     startSignal.countDown();      // let all threads proceed
     doSomethingElse();
     doneSignal.await();           // wait for all to finish
   }
 }

 class Worker implements Runnable {
   private final CountDownLatch startSignal;
   private final CountDownLatch doneSignal;
   Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
     this.startSignal = startSignal;
     this.doneSignal = doneSignal;
   }
   public void run() {
     try {
       startSignal.await();
       doWork();
       doneSignal.countDown();
     } catch (InterruptedException ex) {} // return;
   }

   void doWork() { ... }
 }

這個(gè)案例很直白的說(shuō)明了您机,CountDownLatch可以讓多個(gè)線程同時(shí)初始化完成后等待穿肄,直到主線程要求他們開(kāi)始執(zhí)行為止,并且當(dāng)主線程調(diào)用await()之后阻塞直到所有的線程調(diào)用countDown()將計(jì)數(shù)減為0际看,主線程再次喚醒執(zhí)行后序操作咸产。

當(dāng)然這樣還有一些其他的注意點(diǎn),譬如子線程被中斷或者子線程的耗時(shí)操作很長(zhǎng)導(dǎo)致主線程一直阻塞等問(wèn)題仲闽。

官方案例二

class Driver2 { // ...                                      
  void main() throws InterruptedException {                 
    CountDownLatch doneSignal = new CountDownLatch(N);      
    Executor e = ...                                        
                                                            
    for (int i = 0; i < N; ++i) // create and start threads 
      e.execute(new WorkerRunnable(doneSignal, i));         
                                                            
    doneSignal.await();           // wait for all to finish 
  }                                                         
}                                                           
                                                            
class WorkerRunnable implements Runnable {                  
  private final CountDownLatch doneSignal;                  
  private final int i;                                      
  WorkerRunnable(CountDownLatch doneSignal, int i) {        
    this.doneSignal = doneSignal;                           
    this.i = i;                                             
  }                                                         
  public void run() {                                       
    try {                                                   
      doWork(i);                                            
      doneSignal.countDown();                               
    } catch (InterruptedException ex) {} // return;         
  }                                                         
                                                            
  void doWork() { ... }                                     
  }
}                                                   

這個(gè)案例是說(shuō)锐朴,當(dāng)一個(gè)問(wèn)題需要被分成n份進(jìn)行處理時(shí),將他們用線程池來(lái)執(zhí)行蔼囊,并讓主線程等待焚志。當(dāng)然官方注釋里還說(shuō)了,如果需要反復(fù)用這種形式來(lái)執(zhí)行一些問(wèn)題時(shí)可以考慮使用CyclicBarrier來(lái)代替CountDownLatch畏鼓,因?yàn)镃ountDownLatch是一次性的計(jì)數(shù)器無(wú)法重置酱酬。

CyclicBarrier

字面意思:可循環(huán)使用的柵欄。主要的作用也是讓指定個(gè)數(shù)的線程到達(dá)目標(biāo)位置后進(jìn)入等到狀態(tài)云矫,等所有的線程都到到目標(biāo)位置后同時(shí)開(kāi)始執(zhí)行膳沽。

構(gòu)造方法有2個(gè)

  1. CyclicBarrier(int parties),其中parties指等待的線程數(shù)目,當(dāng)await線程數(shù)達(dá)到parties時(shí)挑社,線程同時(shí)開(kāi)始執(zhí)行陨界。
  2. CyclicBarrier(int parties, Runnable barrierAction),第二個(gè)參數(shù)指所有線程達(dá)到后執(zhí)行的操作痛阻。

通過(guò)第二個(gè)構(gòu)造方法也可以實(shí)現(xiàn)CountDownLatch功能菌瘪,當(dāng)然這不是CyclicBarrier的目的

再來(lái)看一下到達(dá)目標(biāo)位置時(shí)的等待方法,有2個(gè)重載方法

  1. await()阱当,這個(gè)沒(méi)什么可說(shuō)的俏扩,到達(dá)指定位置后等待
  2. await(long timeout, TimeUnit unit),這個(gè)指到到指定位置后等待一段時(shí)間弊添,如果超時(shí)則繼續(xù)執(zhí)行后序操作录淡。

現(xiàn)在來(lái)看2個(gè)例子說(shuō)明一下使用CyclicBarrier可能出現(xiàn)的問(wèn)題

CyclicBarrier例一

public class CyclicBarrierTest {

    public static void main(String[] args) {

        try {
            final int Num = 5;
            CyclicBarrier cyclicBarrier = new CyclicBarrier(Num);

            for (int i = 0; i < Num - 1; i++) {
                new Thread(new RunnableOne(cyclicBarrier)).start();
            }

            Thread thread = new Thread(new RunnableTwo(cyclicBarrier));
            thread.start();

            Thread.sleep(2000);

            thread.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


    private static class RunnableOne implements Runnable {

        CyclicBarrier mCyclicBarrier;

        RunnableOne(CyclicBarrier cyclicBarrier) {
            mCyclicBarrier = cyclicBarrier;
        }

        @Override
        public void run() {
            try {
                System.out.println("wait in barrier");
                mCyclicBarrier.await();
                System.out.println("finish");
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }

    private static class RunnableTwo implements Runnable {

        CyclicBarrier mCyclicBarrier;

        RunnableTwo(CyclicBarrier cyclicBarrier) {
            mCyclicBarrier = cyclicBarrier;
        }

        @Override
        public void run() {
            try {
                System.out.println("wait in barrier");
                Thread.sleep(5000);
                mCyclicBarrier.await();
                System.out.println("finish");
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }
}

打印結(jié)果如下:

wait in barrier
wait in barrier
wait in barrier
wait in barrier
wait in barrier
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at com.jxx.myjavatest.CyclicBarrierTest$RunnableTwo.run(CyclicBarrierTest.java:65)
    at java.lang.Thread.run(Thread.java:748)

這個(gè)例子的意圖也很簡(jiǎn)單,啟動(dòng)4個(gè)RunnableOne油坝,隨后啟動(dòng)1個(gè)RunnableTwo嫉戚,在所有線程都await()之前其中一個(gè)線程被中斷了,因?yàn)闆](méi)有都await()成功澈圈,其他4個(gè)線程就一直阻塞彼水。
這就提醒我們,要在拋出異常后及時(shí)處理极舔,至少也要讓其他線程能正常執(zhí)行下去。

CyclicBarrier例二

public class CyclicBarrierTest {

    public static void main(String[] args) {

        final int Num = 5;
        CyclicBarrier cyclicBarrier = new CyclicBarrier(Num);

        for (int i = 0; i < Num - 1; i++) {
            new Thread(new RunnableOne(cyclicBarrier)).start();
        }

        Thread thread = new Thread(new RunnableTwo(cyclicBarrier));
        thread.start();
    }


    private static class RunnableOne implements Runnable {

        CyclicBarrier mCyclicBarrier;

        RunnableOne(CyclicBarrier cyclicBarrier) {
            mCyclicBarrier = cyclicBarrier;
        }

        @Override
        public void run() {
            try {
                System.out.println("wait in barrier");
                Thread.sleep(5000);
                mCyclicBarrier.await();
                System.out.println("finish");
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }

    private static class RunnableTwo implements Runnable {

        CyclicBarrier mCyclicBarrier;

        RunnableTwo(CyclicBarrier cyclicBarrier) {
            mCyclicBarrier = cyclicBarrier;
        }

        @Override
        public void run() {
            try {
                System.out.println("wait in barrier");

                mCyclicBarrier.await(2000, TimeUnit.MILLISECONDS);

                System.out.println("finish");
            } catch (InterruptedException | BrokenBarrierException | TimeoutException e) {
                e.printStackTrace();
            }
        }
    }
}

打印如下:

wait in barrier
wait in barrier
wait in barrier
wait in barrier
wait in barrier
java.util.concurrent.TimeoutException
    at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:257)
    at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:435)
    at com.jxx.myjavatest.CyclicBarrierTest$RunnableTwo.run(CyclicBarrierTest.java:61)
    at java.lang.Thread.run(Thread.java:748)
java.util.concurrent.BrokenBarrierException
    at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
    at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
    at com.jxx.myjavatest.CyclicBarrierTest$RunnableOne.run(CyclicBarrierTest.java:40)
    at java.lang.Thread.run(Thread.java:748)
java.util.concurrent.BrokenBarrierException
    at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
    at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
    at com.jxx.myjavatest.CyclicBarrierTest$RunnableOne.run(CyclicBarrierTest.java:40)
    at java.lang.Thread.run(Thread.java:748)
java.util.concurrent.BrokenBarrierException
    at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
    at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
    at com.jxx.myjavatest.CyclicBarrierTest$RunnableOne.run(CyclicBarrierTest.java:40)
    at java.lang.Thread.run(Thread.java:748)
java.util.concurrent.BrokenBarrierException
    at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
    at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
    at com.jxx.myjavatest.CyclicBarrierTest$RunnableOne.run(CyclicBarrierTest.java:40)
    at java.lang.Thread.run(Thread.java:748)

這里模擬了一個(gè)await()超時(shí)的異常链瓦,可以看到在拋出異常后需要我們自己處理后期的事物拆魏。同時(shí)某一個(gè)線程拋出超時(shí)異常后,其他線程再次到達(dá)會(huì)拋出BrokenBarrierException異常慈俯,防止繼續(xù)等待渤刃。

Semaphore

其實(shí)Semaphore不該放到這里講,因?yàn)镾emaphore類似于Lock的存在贴膘,是對(duì)資源或者線程的一種控制卖子,但是這篇博文主要講了線程的等待喚起,信號(hào)量放這里講問(wèn)題也不大刑峡。

官方的說(shuō)法是信號(hào)量通常用來(lái)限制線程的數(shù)量洋闽,而不是控制訪問(wèn)一些(物理或邏輯)資源。用法也非常簡(jiǎn)單突梦,使用前先acquire()獲取許可诫舅,在獲取許可過(guò)程中,是線程是被阻塞的宫患,使用完畢release()許可即可刊懈。這點(diǎn)類似于Lock,不同的是Semaphore的acquire()可以被允許多次。

Semaphore有兩個(gè)構(gòu)造方法虚汛,可以指定Semaphore獲取是公平的還是非公平的匾浪,默認(rèn)是非公平

看這里,舉個(gè)栗子:

public class SemaphoreTest {

    public static void main(String[] args) {
        CountDownLatch startLatch = new CountDownLatch(1);
        Semaphore semaphore = new Semaphore(3);

        for (int i = 0; i < 10; i++) {
            new Thread(new MyRunnable(startLatch, semaphore)).start();
        }

        startLatch.countDown();
    }

    private static class MyRunnable implements Runnable {

        final CountDownLatch mCountDownLatch;
        final Semaphore mSemaphore;

        MyRunnable(CountDownLatch countDownLatch, Semaphore semaphore) {
            mCountDownLatch = countDownLatch;
            mSemaphore = semaphore;
        }

        @Override
        public void run() {
            try {
                mCountDownLatch.await();
                mSemaphore.acquire();
                System.out.println(Thread.currentThread().getName() + " acquire success");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                mSemaphore.release();
            }
        }
    }
}

打印如下:

Thread-0 acquire success
Thread-1 acquire success
Thread-9 acquire success
Thread-3 acquire success
Thread-2 acquire success
Thread-4 acquire success
Thread-6 acquire success
Thread-7 acquire success
Thread-5 acquire success
Thread-8 acquire success

可以看出這是默認(rèn)的非公平鎖的情況卷哩,再來(lái)看一下公平鎖的情況

public class SemaphoreTest {

    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3, true);

        for (int i = 0; i < 10; i++) {
            new Thread(new MyRunnable(semaphore)).start();
        }
    }

    private static class MyRunnable implements Runnable {

        final Semaphore mSemaphore;

        MyRunnable(Semaphore semaphore) {
            mSemaphore = semaphore;
        }

        @Override
        public void run() {
            try {
                mSemaphore.acquire();
                System.out.println(Thread.currentThread().getName() + " acquire success");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                mSemaphore.release();
            }
        }
    }
}

打印如下

Thread-0 acquire success
Thread-1 acquire success
Thread-2 acquire success
Thread-3 acquire success
Thread-4 acquire success
Thread-5 acquire success
Thread-6 acquire success
Thread-7 acquire success
Thread-8 acquire success
Thread-9 acquire success

當(dāng)然這里肯定有讀者想了蛋辈,直接將Semaphore置為true公平鎖的情況就好了,何必去掉CountDownLatch呢殉疼。

這里需要注意下梯浪,雖然你Semaphore是公平,但是CountDownLatch到點(diǎn)之后喚起線程的順序是隨機(jī)的瓢娜,并不一定就是線程入隊(duì)的順序喚起挂洛。

線程的join()

jion方法的作用是讓主線程阻塞等待子線程完成,當(dāng)然有幾個(gè)前提條件眠砾,下面細(xì)說(shuō)虏劲。

join方法有三個(gè)重載的版本

  1. final void join(); //一直等待到j(luò)oin的線程執(zhí)行完畢
  2. final synchronized void join(long millis); //等待指定時(shí)間后繼續(xù)執(zhí)行
  3. final synchronized void join(long millis, int nanos); 同上,時(shí)間處理了一下

第一個(gè)和第三個(gè)最后其實(shí)調(diào)用的都是第二個(gè)重載方法褒颈,我們來(lái)看一下源碼

    public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

直接看最后的while循環(huán)柒巫,可以看到,調(diào)用這個(gè)方法谷丸,其實(shí)是調(diào)用Object提供的wait(long timeout)讓主線程阻塞而已堡掏。有幾個(gè)注意點(diǎn)

  1. 子線程如果已經(jīng)銷毀,則直接跳過(guò)等待
  2. join(long millis) 是一個(gè)同步方位刨疼,意味著要想調(diào)用此方法需要先獲取到子線程的實(shí)例對(duì)象鎖

來(lái)看一個(gè)例子泉唁,驗(yàn)證一下第二點(diǎn):

public class JoinTest {

    public static void main(String[] args) {

        final Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {

                try {
                    Thread.sleep(6000);
                    System.out.println("4---" + System.currentTimeMillis());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        thread.start();

        try {
            System.out.println("1---" + System.currentTimeMillis());
            new Thread(new MyRunnable(thread)).start();
            System.out.println("2---" + System.currentTimeMillis());
            thread.join(2000);
            System.out.println("3---" + System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("finish " + System.currentTimeMillis());
    }

    private static class MyRunnable implements Runnable {
        final Object mObject;

        MyRunnable(Object object) {
            mObject = object;
        }


        @Override
        public void run() {

            synchronized (mObject) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}

打印如下:

1---1525529803445
2---1525529803446
3---1525529807449
finish 1525529807449
4---1525529809445

可以很清晰的看到,打印完1之后立即打印了2揩慕,但是2和3之間打相差了4秒亭畜,原因就在join之前需要先獲取thread的鎖對(duì)象,但是需要MyRunnable釋放鎖之后才能執(zhí)行迎卤。

總結(jié)

好了拴鸵,又到總結(jié)的時(shí)間了。

  1. CountDownLatch相對(duì)于CyclicBarrier側(cè)重點(diǎn)是蜗搔,等待其他線程操作完成后主線程在繼續(xù)后續(xù)的操作
  2. CyclicBarrier相對(duì)于CountDownLatch側(cè)重點(diǎn)是劲藐,所有的線程操作完成后等待一起繼續(xù)后續(xù)操作。
  3. CountDownLatch不能重置狀態(tài)樟凄,CyclicBarrier可以重置后多次利用
  4. CountDownLatch和CyclicBarrier拋出異常后都需要妥善處理
  5. Semaphore于Lock類似瘩燥,主要用于線程的訪問(wèn)控制,構(gòu)造時(shí)可以指定是否是公平競(jìng)爭(zhēng)
  6. thread.join()主要是讓主線程等待子線程執(zhí)行完畢不同,有個(gè)注意點(diǎn)就是join()執(zhí)行之前需要獲取到子線程的實(shí)例對(duì)象鎖厉膀。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末溶耘,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子服鹅,更是在濱河造成了極大的恐慌凳兵,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件企软,死亡現(xiàn)場(chǎng)離奇詭異庐扫,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)仗哨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門形庭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人厌漂,你說(shuō)我怎么就攤上這事萨醒。” “怎么了苇倡?”我有些...
    開(kāi)封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵富纸,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我旨椒,道長(zhǎng)晓褪,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任综慎,我火速辦了婚禮涣仿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘示惊。我一直安慰自己好港,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布涝涤。 她就那樣靜靜地躺著,像睡著了一般岛杀。 火紅的嫁衣襯著肌膚如雪阔拳。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天类嗤,我揣著相機(jī)與錄音糊肠,去河邊找鬼。 笑死遗锣,一個(gè)胖子當(dāng)著我的面吹牛货裹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播精偿,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼弧圆,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼赋兵!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起搔预,我...
    開(kāi)封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤霹期,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后拯田,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體历造,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年船庇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了吭产。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡鸭轮,死狀恐怖臣淤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情张弛,我是刑警寧澤荒典,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站吞鸭,受9級(jí)特大地震影響寺董,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜刻剥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一遮咖、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧造虏,春花似錦御吞、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至享钞,卻和暖如春揍诽,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背栗竖。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工暑脆, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人狐肢。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓添吗,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親份名。 傳聞我的和親對(duì)象是個(gè)殘疾皇子碟联,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

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

  • 一妓美、多線程 說(shuō)明下線程的狀態(tài) java中的線程一共有 5 種狀態(tài)。 NEW:這種情況指的是玄帕,通過(guò) New 關(guān)鍵字創(chuàng)...
    Java旅行者閱讀 4,656評(píng)論 0 44
  • 一 因?yàn)轳R頔的《南山南》部脚,才開(kāi)始關(guān)注民謠,因?yàn)榇蟊退臅畬?duì)民謠有了更深的了解裤纹,因?yàn)樾@廣播的及時(shí)更歌委刘,民謠成為...
    陳小蟹閱讀 1,934評(píng)論 20 43
  • 做To B產(chǎn)品時(shí),會(huì)接到很多利益相關(guān)方的需求鹰椒,如果沒(méi)有深入調(diào)研用戶锡移,做出的產(chǎn)品往往差強(qiáng)人意。那么在做產(chǎn)品時(shí)漆际,...
    余田閱讀 296評(píng)論 2 2
  • 清晨淆珊,起了個(gè)大早。妻子說(shuō)奸汇,今天去游大皇宮施符,由于排隊(duì)購(gòu)買門票的游客較多,所以必須趕早擂找。 上了網(wǎng)約車戳吝,可惜天不遂人愿,...
    陳寧兒童與家庭教育研究院閱讀 275評(píng)論 0 3
  • 1贯涎、早晨在地鐵上忽然看到地鐵門玻璃映出來(lái)的自己听哭,玻璃中是一個(gè)專業(yè)氣質(zhì)的經(jīng)理人:干練的短發(fā)、相宜的淡妝塘雳、文藝的框架眼...
    小頤媽閱讀 115評(píng)論 0 0