jdk1.8源碼Thread與Runnable區別


一、概念

Thread實現了Runnable接口

 1 public class Thread implements Runnable {
 2     /* Make sure registerNatives is the first thing <clinit> does. */
 3     private static native void registerNatives();
 4     static {
 5         registerNatives();
 6     }
 7 
 8     private volatile char  name[];
 9     //表示線程的優先級(最大值為10,最小值為1,默認值為5),
10     private int            priority;
11   
12 
13     /* Whether or not the thread is a daemon thread. */
14     //表示線程是否是守護線程,如果在main線程中創建了一個守護線程,
15     //當main方法運行完畢之后,守護線程也會隨着消亡。在JVM中,垃圾收集器線程就是守護線程。
16     private boolean     daemon = false;
17 
18 
19     /* What will be run. */
20     //表示要執行的任務。
21     private Runnable target;
22    。。。。。。    
23 }

二、創建,啟動線程的方法有兩種:

1,繼承Thread

 1 class PrimeThread extends Thread {
 2      long minPrime;
 3      PrimeThread(long minPrime) {
 4          this.minPrime = minPrime;
 5      }
 6 
 7      public void run() {
 8          // compute primes larger than minPrime
 9           . . .
10      }
11  }
12 
13  PrimeThread p = new PrimeThread(143);
14  p.start();

2,實現Runnable 

 1 class PrimeRun implements Runnable {
 2  long minPrime;
 3  PrimeRun(long minPrime) {
 4      this.minPrime = minPrime;
 5  }
 6 
 7  public void run() {
 8      // compute primes larger than minPrime
 9       . . .
10  }
11 }
12 
13 
14 PrimeRun p = new PrimeRun(143);
15 new Thread(p).start();

3,分析

但都調用thread的start()啟動線程,API的注解是:

Causes this thread to begin execution; the Java Virtual Machine calls the <code>run</code> method of this thread.(使該線程開始執行;Java 虛擬機調用該線程的 run 方法。)

用start方法來啟動線程,真正實現了多線程運行,這時無需等待run方法體代碼執行完畢而直接繼續執行下面的代碼。通過調用Thread類的start()方法來啟動一個線程,這時此線程處於就緒(可運行)狀態,並沒有運行,一旦得到cpu時間片,就開始執行run()方法,這里方法 run()稱為線程體,它包含了要執行的這個線程的內容,Run方法運行結束,此線程隨即終止。

而thread的run()方法只是一個普通方法而已,API的注解是:

If this thread was constructed using a separate <code>Runnable</code> run object, then that <code>Runnable</code> object's <code>run</code> method is called;otherwise, this method does nothing and returns.(如果該線程是使用獨立的 Runnable 運行對象構造的,則調用該 Runnable 對象的 run 方法;否則,該方法不執行任何操作並返回。)

如果直接調用Run方法,程序中依然只有主線程這一個線程,其程序執行路徑還是只有一條,還是要順序執行,還是要等待run方法體執行完畢后才可繼續執行下面的代碼,這樣就沒有達到寫線程的目的。

 1 public class ThreadTest extends Thread{
 2 
 3     @Override
 4     public void run() {
 5         System.out.println("Thread Start !");
 6     }
 7 
 8     public static void main(String[] args) {
 9         ThreadTest threadTest = new ThreadTest();
10         threadTest.start();
11         System.out.println("不用等待!");
12     }
13 }

結果:

1 不用等待!
2 Thread Start !

 

 1 public class ThreadTest extends Thread{
 2 
 3     @Override
 4     public void run() {
 5         System.out.println("Thread Start !");
 6     }
 7 
 8     public static void main(String[] args) {
 9         ThreadTest threadTest = new ThreadTest();
10         threadTest.run();
11         System.out.println("不用等待!");
12     }
13 }

結果:

1 Thread Start !
2 不用等待!

 參考博文鏈接:  https://www.cnblogs.com/renhui/p/6066750.html

三、關於賣票的問題:

1,extends Thread

 1 public class MyThread extends Thread {
 2     private int tickets = 10;
 3     @Override
 4     public void run() {
 5 
 6         for (int i = 0; i <= 100; i++) {
 7             if(tickets>0){
 8                 System.out.println(Thread.currentThread().getName()+"--賣出票:" + tickets--);
 9             }
10         }
11     }
12     public static void main(String[] args) {
13         MyThread thread1 = new MyThread();
14         MyThread thread2 = new MyThread();
15         MyThread thread3 = new MyThread();
16 
17         thread1.start();
18         thread2.start();
19         thread3.start();
20     }
21 }

結果:

 1 Thread-0--賣出票:10
 2 Thread-2--賣出票:10
 3 Thread-2--賣出票:9
 4 Thread-2--賣出票:8
 5 Thread-2--賣出票:7
 6 Thread-2--賣出票:6
 7 Thread-1--賣出票:10
 8 Thread-1--賣出票:9
 9 Thread-2--賣出票:5
10 Thread-0--賣出票:9
11 Thread-0--賣出票:8
12 Thread-0--賣出票:7
13 Thread-0--賣出票:6
14 Thread-0--賣出票:5
15 Thread-0--賣出票:4
16 Thread-0--賣出票:3
17 Thread-0--賣出票:2
18 Thread-0--賣出票:1
19 Thread-2--賣出票:4
20 Thread-1--賣出票:8
21 Thread-2--賣出票:3
22 Thread-1--賣出票:7
23 Thread-2--賣出票:2
24 Thread-1--賣出票:6
25 Thread-2--賣出票:1
26 Thread-1--賣出票:5
27 Thread-1--賣出票:4
28 Thread-1--賣出票:3
29 Thread-1--賣出票:2
30 Thread-1--賣出票:1

問題:每個線程都獨立,不共享資源,每個線程都賣出了10張票,總共賣出了30張。

2,implements Runnable

 1 public class MyRunnable implements Runnable {
 2 
 3     private int tickets = 64100;
 4     @Override
 5     public void run() {
 6         for (int i = 0; i <= 64200; i++) {
 7             if(tickets>0){
 8                 System.out.println(Thread.currentThread().getName()+"--賣出票:"+ tickets-- );
 9             }
10         }
11     }
12 
13     public static void main(String[] args) {
14         MyRunnable myRunnable = new MyRunnable();
15         Thread thread1 = new Thread(myRunnable, "窗口一");
16         Thread thread2 = new Thread(myRunnable, "窗口二");
17         Thread thread3 = new Thread(myRunnable, "窗口三");
18 
19         thread1.start();
20         thread2.start();
21         thread3.start();
22     }
23 }

問題:每個線程共享了對象myRunnable的資源,但是當tickets足夠大的時候就會出現一張票被賣出去多次的問題,原因是:讀取共享變量tickets和減一這兩個動作是原子操作,但這兩個動作不可能用一條指令完成,一旦在這兩個動作之間發生線程的切換,同一個值就會被讀取2次,從而發生錯誤!

解決方案就是在方法上加鎖:

1 synchronized (this) {
2     if (tickets > 0) {
3         System.out.println(Thread.currentThread().getName() + "--賣出票:" + tickets--);
4     }
5 }

 四、拓展

 關於Thread的詳細源碼剖析可參考https://www.cnblogs.com/dennyzhangdd/p/7280032.html

 關於Thread多次start一個線程報錯:IllegalThreadStateException()

 

----------------------------------------------多做多解決多總結-----------------------------------

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM