Java線程相關(guān)學(xué)習(xí)筆記

Java 線程相關(guān)

  1. 如何創(chuàng)建線程(兩種方式,區(qū)別,使用場景)
  2. 線程狀態(tài)調(diào)度
  3. 多線程數(shù)據(jù)共享(會有什么問題,如何實現(xiàn)共享,多線程操作同一個變量會有什么問題,如果不希望有問題怎么做)
  4. 數(shù)據(jù)傳遞
  5. 線程池相關(guān)(如何創(chuàng)建線程池,要注意什么(初始化線程內(nèi)部變量),幾種常用的使用方式)

1. 線程創(chuàng)建

通常創(chuàng)建線程有兩種方式,一個是繼承 Thread, 一個是實現(xiàn) Runnable; 下面則分別實現(xiàn)以做演示,然后說一下這兩種的區(qū)別,應(yīng)該如何選擇

創(chuàng)建線程

創(chuàng)建線程和使用的一個小case如下, 注意的是線程啟動是調(diào)用start方法, 而不是 run 方法; 其次實現(xiàn)Runnable 接口的類,啟動依然是放在一個Thread 對象中

public class ThreadCreate {


    /**
     * 通過繼承  Thread 方式來創(chuàng)建一個新的線程
     */
    public static class ThreadExtend extends Thread {

        @Test
        public void run() {
            System.out.println("new extend thread");
        }
    }


    /**
     * 通過實現(xiàn) Runnable 方式來創(chuàng)建一個線程
     */
    public static class RunnableImplement implements Runnable {

        @Override
        public void run() {
            System.out.println("new runnable thread");
        }
    }

    @Test
    public void testCreate() {
        new ThreadExtend().start();

        new Thread(new RunnableImplement()).start();

        System.out.println("main!");
    }

}

兩種方式對比

為什么會有兩種方式呢?這兩種的區(qū)別何在?

  1. 實現(xiàn)是可以有多個的,但是繼承只能有一個父類
  2. 查看 Runnable 的使用方法,最終是放在一個 Thread里面去執(zhí)行的,所以在多個相同的程序代碼處理一個資源時,這個還是有優(yōu)勢的;但是查看 Thread實際上就是 Runnable的實現(xiàn),同樣可以將一個自定義的Thread對象,創(chuàng)建多個 Thread對象來調(diào)用

通過上面的描述可以知道一點(diǎn),如果你希望數(shù)據(jù)多線程內(nèi)共享,不妨考慮實現(xiàn) Runnable 接口(當(dāng)然繼承Thread也是ok的);如果希望隔離,則不妨考慮繼承Thread (實際上使用 Runnable接口的實現(xiàn)也是ok的,多創(chuàng)建幾個實現(xiàn)類接口對象而已,每個對象放在一個新的Thread中執(zhí)行)

按照個人的理解,網(wǎng)上說的實現(xiàn)Runnable 方便資源共享,更多的是傾向于代碼的共享,通常是一個Runnable對象,放在多個 Thread實例中執(zhí)行;而繼承 Thead類,從出發(fā)點(diǎn)來看,繼承的一般是作為一個獨(dú)立線程來執(zhí)行使用,如果你真要像下面這么做,也不會報錯,也能正常運(yùn)行,只是有點(diǎn)違反設(shè)計理念而已

MyThread extreds Thread {...};
MyThread mythread = new MyThread();
new Thread(mythread).start();

case 舉例

舉一個例子,車站賣票,假設(shè)現(xiàn)在有三個窗口,總共只有30張車票,賣完就不賣了,怎么實現(xiàn)?如果每個窗口有10張車票,各個窗口把自己的賣完了就不賣了,怎么實現(xiàn)?

第一個case,符合數(shù)據(jù)共享的一種場景,那么我們的實現(xiàn)可以如下:

public static class TotalSaleTick implements Runnable {
   private int total = 30;

   @Override
   public void run() {
       while (true) {
           if (total > 0) {
               System.out.println(Thread.currentThread().getName() + "售出一張,剩余:" + --total);
           } else {
               break;
           }
       }
   }
}


@Test
public void testTotalSale() {
   TotalSaleTick totalSaleTick = new TotalSaleTick();
   Thread thread1 = new Thread(totalSaleTick, "窗口1");
   Thread thread2 = new Thread(totalSaleTick, "窗口2");
   Thread thread3 = new Thread(totalSaleTick, "窗口3");

   thread1.start();
   thread2.start();
   thread3.start();
   System.out.println("master over!");
}

輸出如下, 基本上每次跑的輸出結(jié)果都不一樣, 可以看出的一點(diǎn)是三個窗口售出的票數(shù)不同,一個問題,上面這種情況,可能造成超賣么?

窗口1售出一張,剩余:29
master over!
窗口2售出一張,剩余:28
窗口2售出一張,剩余:25
窗口2售出一張,剩余:24
窗口1售出一張,剩余:27
窗口3售出一張,剩余:26
窗口1售出一張,剩余:22
窗口1售出一張,剩余:20
窗口1售出一張,剩余:19
窗口1售出一張,剩余:18
窗口1售出一張,剩余:17
窗口1售出一張,剩余:16
窗口1售出一張,剩余:15
窗口1售出一張,剩余:14
窗口1售出一張,剩余:13
窗口1售出一張,剩余:12
窗口1售出一張,剩余:11
窗口1售出一張,剩余:10
窗口2售出一張,剩余:23
窗口2售出一張,剩余:8
窗口2售出一張,剩余:7
窗口2售出一張,剩余:6
窗口2售出一張,剩余:5
窗口2售出一張,剩余:4
窗口1售出一張,剩余:9
窗口3售出一張,剩余:21
窗口3售出一張,剩余:1
窗口3售出一張,剩余:0
窗口1售出一張,剩余:2
窗口2售出一張,剩余:3

第二個case,則顯然更傾向于繼承 Thread 來實現(xiàn)了

public static class SplitSaleTick extends Thread {
   private int total = 10;

   public SplitSaleTick(String name) {
       super(name);
   }

   @Override
   public void run() {
       while (true) {
           if (total > 0) {
               System.out.println(Thread.currentThread().getName() + "售出一張,剩余:" + --total);
           } else {
               break;
           }
       }
   }
}


@Test
public void testSplitSaleTick() {
   SplitSaleTick splitSaleTick1 = new SplitSaleTick("窗口1");
   SplitSaleTick splitSaleTick2 = new SplitSaleTick("窗口2");
   SplitSaleTick splitSaleTick3 = new SplitSaleTick("窗口3");

   splitSaleTick1.start();
   splitSaleTick2.start();
   splitSaleTick3.start();
   System.out.println("master over");
}


/**
* 繼承 Thread 也可以實現(xiàn)共享, 只不過比較惡心而已
*/
@Test
public void testSplitSaleTick2() {
   SplitSaleTick splitSaleTick1 = new SplitSaleTick("saleTick");

   Thread thread1 = new Thread(splitSaleTick1, "窗口1");
   Thread thread2 = new Thread(splitSaleTick1, "窗口2");
   Thread thread3 = new Thread(splitSaleTick1, "窗口3");

   thread1.start();
   thread2.start();
   thread3.start();
}

輸出接過如下, 三個窗口可以并發(fā)賣,且每個窗口賣10張,賣完即止

窗口1售出一張,剩余:9
窗口2售出一張,剩余:9
窗口2售出一張,剩余:8
窗口1售出一張,剩余:8
窗口1售出一張,剩余:7
窗口1售出一張,剩余:6
窗口1售出一張,剩余:5
窗口1售出一張,剩余:4
窗口2售出一張,剩余:7
窗口1售出一張,剩余:3
窗口1售出一張,剩余:2
窗口1售出一張,剩余:1
窗口1售出一張,剩余:0
窗口3售出一張,剩余:9
窗口3售出一張,剩余:8
窗口3售出一張,剩余:7
窗口3售出一張,剩余:6
窗口3售出一張,剩余:5
窗口3售出一張,剩余:4
窗口3售出一張,剩余:3
窗口3售出一張,剩余:2
窗口3售出一張,剩余:1
窗口3售出一張,剩余:0
master over
窗口2售出一張,剩余:6
窗口2售出一張,剩余:5
窗口2售出一張,剩余:4
窗口2售出一張,剩余:3
窗口2售出一張,剩余:2
窗口2售出一張,剩余:1
窗口2售出一張,剩余:0


---- test2 輸出 ----
窗口1售出一張,剩余:9
窗口1售出一張,剩余:6
窗口1售出一張,剩余:5
窗口1售出一張,剩余:4
窗口1售出一張,剩余:3
窗口1售出一張,剩余:2
窗口3售出一張,剩余:7
窗口2售出一張,剩余:8
窗口3售出一張,剩余:0
窗口1售出一張,剩余:1

2. 線程狀態(tài)(線程生命周期)

線程創(chuàng)建之后,即調(diào)用了start方法之后,線程是否開始運(yùn)行了?這個運(yùn)行過程是否會暫停呢?如果需要暫停應(yīng)該怎么辦;如果一個線程依賴另一個線程的計算結(jié)果,又該如何處理?

image.png
  • 創(chuàng)建:新建一個線程對象,如Thread thd=new Thread()
  • 就緒:創(chuàng)建了線程對象后,調(diào)用了線程的start()方法(此時線程知識進(jìn)入了線程隊列,等待獲取CPU服務(wù) ,具備了運(yùn)行的條件,但并不一定已經(jīng)開始運(yùn)行了)
  • 運(yùn)行:處于就緒狀態(tài)的線程,一旦獲取了CPU資源,便進(jìn)入到運(yùn)行狀態(tài),開始執(zhí)行run()方法里面的邏輯
  • 終止:線程的run()方法執(zhí)行完畢,或者線程調(diào)用了stop()方法,線程便進(jìn)入終止?fàn)顟B(tài)
  • 阻塞:一個正在執(zhí)行的線程在某些情況系,由于某種原因而暫時讓出了CPU資源,暫停了自己的執(zhí)行,便進(jìn)入了阻塞狀態(tài),如調(diào)用了sleep()方法
  • 線程讓步: join 等待其他線程終止。在當(dāng)前線程中調(diào)用另一個線程的join()方法,則當(dāng)前線程轉(zhuǎn)入阻塞狀態(tài),直到另一個進(jìn)程運(yùn)行結(jié)束,當(dāng)前線程再由阻塞轉(zhuǎn)為就緒狀態(tài)

3. 方法說明

一個Thread實例有一些常用的方法如: start, sleep, run, yield, join, wait 等, 這些方法是干嘛用的,什么場景下使用,使用時需要注意些什么?

方法的執(zhí)行,將對應(yīng)線程狀態(tài)進(jìn)行說明

run 方法

run 方法中為具體的線程執(zhí)行的代碼邏輯,一般而言,都不應(yīng)該被直接進(jìn)行調(diào)用

無論我們采用哪種方法創(chuàng)建線程,基本上都是要重寫run 方法,這個方法會在線程執(zhí)行時調(diào)用

start 方法

執(zhí)行該方法之后,線程進(jìn)入就緒狀態(tài),對使用者而言,希望線程執(zhí)行就是調(diào)用的這個方法(注意調(diào)用之后不會立即執(zhí)行)

這個方法的主要目的就是告訴系統(tǒng),我們的線程準(zhǔn)備好了,cpu有空了趕緊來執(zhí)行我們的線程

sleep 方法

睡眠一段時間,這個過程中不會釋放線程持有的鎖, 傳入int類型的參數(shù),表示睡眠多少ms

讓出CUP的使用、目的是不讓當(dāng)前線程獨(dú)自霸占該進(jìn)程所獲的CPU資源,以留一定時間給其他線程執(zhí)行的機(jī)會

我們最常見的一種使用方式是在主線程中直接調(diào)用 Thread.sleep(100) , 表示先等個100ms, 然后再繼續(xù)執(zhí)行

wait 方法

wait()方法是Object類里的方法;當(dāng)一個線程執(zhí)行到wait()方法時,它就進(jìn)入到一個和該對象相關(guān)的等待池中,同時失去(釋放)了對象的機(jī)鎖(暫時失去機(jī)鎖,wait(long timeout)超時時間到后還需要返還對象鎖);其他線程可以訪問

wait()使用notify或者notifyAlll或者指定睡眠時間來喚醒當(dāng)前等待池中的線程

通常我們執(zhí)行wait方法是因為當(dāng)前線程的執(zhí)行,可能依賴到其他線程,如登錄線程中,若發(fā)現(xiàn)用戶沒有注冊,則等待,等用戶注冊成功后繼續(xù)走登錄流程(我們不考慮這個邏輯是否符合實際),

這里就可以在登錄線程中調(diào)用 wait方法, 在注冊線程中,在執(zhí)行完畢之后,調(diào)用notify方法通知登錄線程,注冊完畢,然后繼續(xù)進(jìn)行登錄后續(xù)action

yield 方法

暫停當(dāng)前正在執(zhí)行的線程對象,并執(zhí)行其他線程

yield()應(yīng)該做的是讓當(dāng)前運(yùn)行線程回到可運(yùn)行狀態(tài),以允許具有相同優(yōu)先級的其他線程獲得運(yùn)行機(jī)會。因此,使用yield()的目的是讓相同優(yōu)先級的線程之間能適當(dāng)?shù)妮嗈D(zhuǎn)執(zhí)行。但是,實際中無法保證yield()達(dá)到讓步目的,因為讓步的線程還有可能被線程調(diào)度程序再次選中

這個方法的執(zhí)行,有點(diǎn)像一個拿到面包的人對另外幾個人說,我把面包放在桌上,我們從新開始搶,那么下一個拿到面包的還是這些人中的某個(大家機(jī)會均等)

想象不出啥時候會這么干

join 方法

啟動線程后直接調(diào)用,即join()的作用是:“等待該線程終止”,這里需要理解的就是該線程是指的主線程等待子線程的終止。也就是在子線程調(diào)用了join()方法后面的代碼,只有等到子線程結(jié)束了才能執(zhí)行

從上面的描述也可以很容易看出什么場景需要調(diào)用這個方法,主線程和子線程誰先結(jié)束不好說,如果主線程提前結(jié)束了,導(dǎo)致整個應(yīng)用都關(guān)了,這個時候子線程沒執(zhí)行完,就呵呵了;其次就是子線程執(zhí)行一系列計算,主線程會用到計算結(jié)果,那么就可以執(zhí)行這個方法,保證子線程執(zhí)行完畢后再使用計算結(jié)果

4. 數(shù)據(jù)共享

多線程間數(shù)據(jù)共享,當(dāng)多線程公用一個Runnable對象時,這個對象中的成員變量即可以達(dá)到數(shù)據(jù)共享的目的;多線程采用不同的Runnable對象時,數(shù)據(jù)怎么共享

公用 Runnable對象時

上面的售票例子中,其實就有這個場景,上面提出了一個問題,是否會出現(xiàn)超賣的情況?

  1. 因為我們知道 ++ 不是原子操作, 實際可以拆分為三步:

    • 內(nèi)存到寄存器
    • 寄存器自增
    • 寫回內(nèi)存

    假設(shè)num為10時, 線程A和線程B都調(diào)用 ++num操作;對于內(nèi)存到寄存器這一步,兩個線程都到了這一步,A自增將11寫回內(nèi)存,B也進(jìn)行自增將11寫會內(nèi)存,這個時候就少+1了

  2. 讀一個long,double類型的共享變量時,也不是原子操作,在32位操作系統(tǒng)上對64位的數(shù)據(jù)的讀寫要分兩步完成,每一步取32位數(shù)據(jù),如果有兩個線程同時寫一個變量內(nèi)存,一個進(jìn)程寫低32位,而另一個寫高32位,這樣將導(dǎo)致獲取的64位數(shù)據(jù)是失效的數(shù)據(jù)

在多線程中,共享數(shù)據(jù)的獲取or更新,請確保是原子操作;可以考慮同步鎖(synchronized)修改共享變量,共享變量前添加volatile, 使用原子數(shù)據(jù)類型 AtomicInteger

修改上面的售票代碼如下

public static class TotalSaleTick implements Runnable {
        private int total = 30;

        @Override
        public void run() {
            while (true) {
                synchronized (this) {
                    if (total > 0) {
                        System.out.println(Thread.currentThread().getName() + "售出一張,剩余:" + --total);
                    } else {
                        break;
                    }
                }
            }
        }
    }

一個小疑惑,在實際的測試中,即便是上面不加上同步塊,好像也沒有出問題,對于上面的操作可能運(yùn)行很多遍都是正確的, 好像和我們預(yù)期的不相符,有沒有可能是因為總數(shù)太少,導(dǎo)致沖突的機(jī)率變小了?

private AtomicInteger count = new AtomicInteger(0);
private int sum = 3000;

public class MyThread extends Thread {
   public void run() {
       while (true) {
           if (sum > 0) {
               count.addAndGet(1);
               --sum;
           }else {
               break;
           }
       }
       System.out.println(Thread.currentThread().getName() + " over " + sum);
   }
}


@Test
public void testAdd() throws InterruptedException {
   MyThread myThread1 = new MyThread();
   MyThread myThread2 = new MyThread();

   myThread1.start();
   myThread2.start();

   myThread1.join();
   myThread2.join();

   System.out.println("num: " + sum + " count: " + count.get());
}

對上面的場景,多運(yùn)行幾次,發(fā)現(xiàn)輸出結(jié)果果然是超賣了

Thread-1 over -1
Thread-0 over -1
num: -1 count: 3008

非公用的 Runnable 對象時

共享全局變量 + 共享局部變量兩種情況,有點(diǎn)區(qū)別

上面的case就是一個共享全局變量的demo,上面出現(xiàn)了并發(fā)沖突,可以如下解決, 針對類進(jìn)行加鎖

public class ThreadShareTest {
    private AtomicInteger count = new AtomicInteger(0);
    private int sum = 3000;

    public class MyThread extends Thread {
        public void run() {
            while (true) {
                if (sum > 0) {
                    synchronized (ThreadShareTest.class) {
                        if (sum > 0) {
                            count.addAndGet(1);
                            --sum;
                        }
                    }
                }else {
                    break;
                }
            }
            System.out.println(Thread.currentThread().getName() + " over " + sum);
        }
    }


    @Test
    public void testAdd() throws InterruptedException {
        MyThread myThread1 = new MyThread();
        MyThread myThread2 = new MyThread();

        myThread1.start();
        myThread2.start();

        myThread1.join();
        myThread2.join();

        System.out.println("num: " + sum + " count: " + count.get());
    }
}

共享局部變量,需要注意的是局部變量要求是final, 所以下面的int采用了數(shù)組的形式(基本類型無法修改,引用類型可以改其內(nèi)部的值, 不能改引用)

@Test
public void testAdd2() throws InterruptedException {
   final int[] num = {3000};
   final AtomicInteger c = new AtomicInteger(0);


   Runnable runnable = new Runnable() {
       @Override
       public void run() {
           while (true) {
               if (num[0] > 0) {
                   c.addAndGet(1);
                   num[0]--;
               } else {
                   break;
               }
           }

           System.out.println(Thread.currentThread().getName() + " over " + num[0]);
       }
   };

   Thread thread1 = new Thread(runnable);
   Thread thread2 = new Thread(runnable);

   thread1.start();
   thread2.start();

   thread1.join();
   thread2.join();

   System.out.println("num: " + num[0] + " count: " + c.get());
}

多運(yùn)行幾次,輸出如下,說明也存在并發(fā)的問題了, 修正方式同樣是加鎖

Thread-0 over -1
Thread-1 over -1
num: -1 count: 3001

修改后的run方法內(nèi)部如下

while (true) {
     if (num[0] > 0) {
         synchronized (this) {
             if (num[0] > 0) {
                 c.addAndGet(1);
                 num[0]--;
             } else {
                 break;
             }
         }
     } else {
         break;
     }
 }

線程數(shù)據(jù)隔離

上面是數(shù)據(jù)在多線程中共享,很容易出現(xiàn)的就是并發(fā)問題;還有一個場景就是我希望不存在數(shù)據(jù)共享,線程操作的內(nèi)部變量不影響其他的線程; 最簡單的想法就是一個繼承了Thread的類,其內(nèi)部類正常來講就是隔離的,只要你不把它當(dāng)成 Runnable 接口的使用方式就行

使用 ThreadLocal 來保證變量在線程之間的隔離, 下面是一個簡單的演示,兩個線程都是在修改threadLocal中的值, 但是兩個線程的修改,對彼此而言是獨(dú)立的

public static class LocalT implements Runnable {
   ThreadLocal<Integer> threadLocal = new ThreadLocal<>();

   @Override
   public void run() {
       int start = (int) (Math.random() * 100);
       for (int i =0 ; i < 100; i = i+2) {
           threadLocal.set(start + i);
           System.out.println(Thread.currentThread().getName() + " : " + get());
       }
   }

   public int get() {
       return threadLocal.get();
   }
}


@Test
public void testLocal() throws InterruptedException {
   LocalT local = new LocalT();

   Thread thread1 = new Thread(local);
   Thread thread2 = new Thread(local);

   thread1.start();
   thread2.start();

   thread1.join();
   thread2.join();
}

5. 數(shù)據(jù)傳遞

數(shù)據(jù)如何傳遞給線程,有如何把線程計算的結(jié)果拋出來

傳遞數(shù)據(jù)

比較容易想到的就是在創(chuàng)建對象時,傳入數(shù)據(jù);或者調(diào)用線程對象的setXXX方法傳入數(shù)據(jù), 當(dāng)做正常的對來操作處理即可

需要注意的是,在線程的執(zhí)行期間,你修改了其中的局部變量,會出現(xiàn)什么情況呢?

public static class ThreadData implements Runnable {
   private int num = 0;


   public void run() {
       while (num < 100) {
           System.out.println(Thread.currentThread().getName() + " now: " + num++);
       }

       System.out.println(Thread.currentThread().getName() + " num: " + num);
   }

   public void setNum(int num) {
       System.out.println(this.num + " now set to " + num);
       this.num = num;
   }
}


@Test
public void testThreadSetData() throws InterruptedException {
   ThreadData threadData = new ThreadData();

   Thread thread1 = new Thread(threadData);
   Thread thread2 = new Thread(threadData);

   thread1.start();
   thread2.start();

   threadData.setNum(200);

   thread1.join();
   thread1.join();
}

輸出如下, 將num設(shè)置為200之后,并沒有如我們預(yù)期的結(jié)束線程,依然在往下走, 這里就相當(dāng)于是有一個你修改了這個數(shù)據(jù),是否會立馬就生效呢?特別是對其他的線程而言

...
Thread-1 now: 24
Thread-1 now: 25
Thread-0 now: 14
26 now set to 200
Thread-0 now: 27
Thread-0 now: 28
Thread-1 now: 26
Thread-0 now: 29
Thread-1 now: 30
....

輸出結(jié)果

線程執(zhí)行了一個任務(wù)之后,輸出的結(jié)果可以怎么處理

一個實例,一個線程實現(xiàn)累加的過程,我現(xiàn)在希望實現(xiàn)1 加到 100, 開四個線程,怎么做?

下面是一個實現(xiàn),不知道有沒有什么問題

public static class CalculateThread extends Thread {

   private int start;
   private int end;

   private int ans;

   public CalculateThread(int start, int end) {
       this.start = start;
       this.end = end;
   }

   public void run() {
       for (int i = start; i <= end; i++) {
           ans += i;
       }
   }

   public int getAns() {
       return ans;
   }
}


@Test
public void testCalculate() throws InterruptedException {
   CalculateThread c1 = new CalculateThread(1, 25);
   CalculateThread c2 = new CalculateThread(26, 50);
   CalculateThread c3 = new CalculateThread(51, 75);
   CalculateThread c4 = new CalculateThread(76, 100);

   c1.start();
   c2.start();
   c3.start();
   c4.start();


   c1.join();
   c2.join();
   c3.join();
   c4.join();

   System.out.println("ans1: " + c1.getAns() + " ans2: " + c2.getAns() + " ans3: " + c3.getAns() + " ans4: " + c4.getAns());
   int ans = c1.getAns() + c2.getAns() + c3.getAns() + c4.getAns();
   System.out.println("ans : " + ans);
}

參考

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

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

  • Java多線程學(xué)習(xí) [-] 一擴(kuò)展javalangThread類 二實現(xiàn)javalangRunnable接口 三T...
    影馳閱讀 3,115評論 1 18
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,839評論 18 399
  • 該文章轉(zhuǎn)自:http://blog.csdn.net/evankaka/article/details/44153...
    加來依藍(lán)閱讀 7,472評論 3 87
  • 寫在前面的話: 這篇博客是我從這里“轉(zhuǎn)載”的,為什么轉(zhuǎn)載兩個字加“”呢?因為這絕不是簡單的復(fù)制粘貼,我花了五六個小...
    SmartSean閱讀 4,965評論 12 45
  • 本文主要講了java中多線程的使用方法、線程同步、線程數(shù)據(jù)傳遞、線程狀態(tài)及相應(yīng)的一些線程函數(shù)用法、概述等。 首先講...
    李欣陽閱讀 2,602評論 1 15

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