程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 基於Java回想之多線程詳解

基於Java回想之多線程詳解

編輯:關於JAVA

基於Java回想之多線程詳解。本站提示廣大學習愛好者:(基於Java回想之多線程詳解)文章只能為提供參考,不一定能成為您想要的結果。以下是基於Java回想之多線程詳解正文


線程是操作體系運轉的根本單元,它被封裝在過程中,一個過程可以包括多個線程。即便我們不手動發明線程,過程也會有一個默許的線程在運轉。

關於JVM來講,當我們編寫一個單線程的法式去運轉時,JVM中也是有至多兩個線程在運轉,一個是我們創立的法式,一個是渣滓收受接管。

線程根本信息

我們可以經由過程Thread.currentThread()辦法獲得以後線程的一些信息,並對其停止修正。

我們來看以下代碼:

檢查並修正以後線程的屬性
 String name = Thread.currentThread().getName();
         int priority = Thread.currentThread().getPriority();
         String groupName = Thread.currentThread().getThreadGroup().getName();
         boolean isDaemon = Thread.currentThread().isDaemon();
         System.out.println("Thread Name:" + name);
         System.out.println("Priority:" + priority);
         System.out.println("Group Name:" + groupName);
         System.out.println("IsDaemon:" + isDaemon);

         Thread.currentThread().setName("Test");
         Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
         name = Thread.currentThread().getName();
         priority = Thread.currentThread().getPriority();
         groupName = Thread.currentThread().getThreadGroup().getName();
         isDaemon = Thread.currentThread().isDaemon();
         System.out.println("Thread Name:" + name);
         System.out.println("Priority:" + priority);

個中列出的屬性解釋以下:

    GroupName,每一個線程都邑默許在一個線程組裡,我們也能夠顯式的創立線程組,一個線程組中也能夠包括子線程組,如許線程和線程組,就組成了一個樹狀構造。

    Name,每一個線程都邑有一個名字,假如不顯式指定,那末名字的規矩是“Thread-xxx”。

    Priority,每一個線程都邑有本身的優先級,JVM對優先級的處置方法是“搶占式”的。當JVM發明優先級高的線程時,立時運轉該線程;關於多個優先級相等的線程,JVM對其停止輪詢處置。Java的線程優先級從1到10,默許是5,Thread類界說了2個常量:MIN_PRIORITY和MAX_PRIORITY來表現最高和最低優先級。

    我們可以看上面的代碼,它界說了兩個分歧優先級的線程:

線程優先級示例
 public static void priorityTest()
 {
     Thread thread1 = new Thread("low")
     {
         public void run()
         {
             for (int i = 0; i < 5; i++)
             {
                 System.out.println("Thread 1 is running.");
             }
         }
     };

     Thread thread2 = new Thread("high")
     {
         public void run()
         {
             for (int i = 0; i < 5; i++)
             {
                 System.out.println("Thread 2 is running.");
             }
         }
     };

     thread1.setPriority(Thread.MIN_PRIORITY);
     thread2.setPriority(Thread.MAX_PRIORITY);
     thread1.start();
     thread2.start();
 }

    從運轉成果可以看出,是高優先級線程運轉完成後,低優先級線程才運轉。
    isDaemon,這個屬性用來掌握父子線程的關系,假如設置為true,當父線程停止後,其下一切子線程也停止,反之,子線程的性命周期不受父線程影響。
我們來看上面的例子:

IsDaemon 示例
 public static void daemonTest()
 {
     Thread thread1 = new Thread("daemon")
     {
         public void run()
         {
             Thread subThread = new Thread("sub")
             {
                 public void run()
                 {
                     for(int i = 0; i < 100; i++)
                     {
                         System.out.println("Sub Thread Running " + i);
                     }
                 }
             };
             subThread.setDaemon(true);
             subThread.start();
             System.out.println("Main Thread end.");
         }
     };

     thread1.start();
 }

    下面代碼的運轉成果,在和刪除subThread.setDaemon(true);後比較,可以發明後者運轉進程中子線程會完成履行後再停止,而前者中,子線程很快就停止了。

若何創立線程

下面的內容,都是演示默許線程中的一些信息,那末應當若何創立線程呢?在Java中,我們有3種方法可以用來創立線程。

Java中的線程要末繼續Thread類,要末完成Runnable接口,我們逐個道來。

應用外部類來創立線程

我們可使用外部類的方法來創立線程,進程是聲明一個Thread類型的變量,偏重寫run辦法。示例代碼以下:

應用外部類創立線程
 public static void createThreadByNestClass()
 {
     Thread thread = new Thread()
     {
         public void run()
         {
             for (int i =0; i < 5; i++)
             {
                 System.out.println("Thread " + Thread.currentThread().getName() + " is running.");
             }
             System.out.println("Thread " + Thread.currentThread().getName() + " is finished.");
         }
     };
     thread.start();
 }

繼續Thread以創立線程

我們可以從Thread中派生一個類,重寫其run辦法,這類方法和下面類似。示例代碼以下:

派生Thread類以創立線程
 class MyThread extends Thread
 {
     public void run()
     {
         for (int i =0; i < 5; i++)
         {
             System.out.println("Thread " + Thread.currentThread().getName() + " is running.");
         }
         System.out.println("Thread " + Thread.currentThread().getName() + " is finished.");
     }
 }

 
 public static void createThreadBySubClass()
 {
     MyThread thread = new MyThread();
     thread.start();
 }

完成Runnable接口以創立線程

我們可以界說一個類,使其完成Runnable接口,然後將該類的實例作為構建Thread變量結構函數的參數。示例代碼以下:

完成Runnable接口以創立線程
 class MyRunnable implements Runnable
 {
     public void run()
     {
         for (int i =0; i < 5; i++)
         {
             System.out.println("Thread " + Thread.currentThread().getName() + " is running.");
         }
         System.out.println("Thread " + Thread.currentThread().getName() + " is finished.");
     }
 }

 
 public static void createThreadByRunnable()
 {
     MyRunnable runnable = new MyRunnable();
     Thread thread = new Thread(runnable);
     thread.start();
 }

上述3種方法都可以創立線程,並且從示例代碼上看,線程履行的功效是一樣的,那末這三種創立方法有甚麼分歧呢?

這觸及到Java中多線程的運轉形式,關於Java來講,多線程在運轉時,有“多對象多線程”和“單對象多線程”的差別:

    多對象多線程,法式在運轉進程中創立多個線程對象,每一個對象上運轉一個線程。
    單對象多線程,法式在運轉進程中創立一個線程對象,在其上運轉多個線程。

明顯,從線程同步和調劑的角度來看,多對象多線程要簡略一些。上述3種線程創立方法,前兩種都屬於“多對象多線程”,第三種既可使用“多對象多線程”,也能夠應用“單對象單線程”。

我們來看上面的示例代碼,外面會用到Object.notify辦法,這個辦法會叫醒對象上的一個線程;而Object.notifyAll辦法,則會叫醒對象上的一切線程。

notify示例
 public class NotifySample {

     public static void main(String[] args) throws InterruptedException
     {
         notifyTest();
         notifyTest2();
         notifyTest3();
     }

     private static void notifyTest() throws InterruptedException
     {
         MyThread[] arrThreads = new MyThread[3];
         for (int i = 0; i < arrThreads.length; i++)
         {
             arrThreads[i] = new MyThread();
             arrThreads[i].id = i;
             arrThreads[i].setDaemon(true);
             arrThreads[i].start();
         }
         Thread.sleep(500);
         for (int i = 0; i < arrThreads.length; i++)
         {
             synchronized(arrThreads[i])
             {
                 arrThreads[i].notify();
             }
         }
     }

     private static void notifyTest2() throws InterruptedException
     {
         MyRunner[] arrMyRunners = new MyRunner[3];
         Thread[] arrThreads = new Thread[3];
         for (int i = 0; i < arrThreads.length; i++)
         {
             arrMyRunners[i] = new MyRunner();
             arrMyRunners[i].id = i;
             arrThreads[i] = new Thread(arrMyRunners[i]);
             arrThreads[i].setDaemon(true);
             arrThreads[i].start();
         }
         Thread.sleep(500);
         for (int i = 0; i < arrMyRunners.length; i++)
         {
             synchronized(arrMyRunners[i])
             {
                 arrMyRunners[i].notify();
             }
         }
     }

     private static void notifyTest3() throws InterruptedException
     {
         MyRunner runner = new MyRunner();
         Thread[] arrThreads = new Thread[3];
         for (int i = 0; i < arrThreads.length; i++)
         {
             arrThreads[i] = new Thread(runner);
             arrThreads[i].setDaemon(true);
             arrThreads[i].start();
         }
         Thread.sleep(500);

         synchronized(runner)
         {
             runner.notifyAll();
         }
     }
 }

 class MyThread extends Thread
 {
     public int id = 0;
     public void run()
     {
         System.out.println("第" + id + "個線程預備休眠5分鐘。");
         try
         {
             synchronized(this)
             {
                 this.wait(5*60*1000);
             }
         }
         catch(InterruptedException ex)
         {
             ex.printStackTrace();
         }
         System.out.println("第" + id + "個線程被叫醒。");
     }
 }

 class MyRunner implements Runnable
 {
     public int id = 0;
     public void run()
     {
         System.out.println("第" + id + "個線程預備休眠5分鐘。");
         try
         {
             synchronized(this)
             {
                 this.wait(5*60*1000);
             }
         }
         catch(InterruptedException ex)
         {
             ex.printStackTrace();
         }
         System.out.println("第" + id + "個線程被叫醒。");
     }

 }

示例代碼中,notifyTest()和notifyTest2()是“多對象多線程”,雖然notifyTest2()中的線程完成了Runnable接口,然則它外面界說Thread數組時,每一個元素都應用了一個新的Runnable實例。notifyTest3()屬於“單對象多線程”,由於我們只界說了一個Runnable實例,一切的線程都邑應用這個實例。

notifyAll辦法實用於“單對象多線程”的情形,由於notify辦法只會隨機叫醒對象上的一個線程。

線程的狀況切換

關於線程來說,從我們創立它一向到線程運轉停止,在這個進程中,線程的狀況能夠是如許的:

    創立:曾經有Thread實例了, 然則CPU還無為其分派資本和時光片。
    停當:線程曾經取得了運轉所需的一切資本,只等CPU停止時光調劑。
    運轉:線程位於以後CPU時光片中,正在履行相干邏輯。
    休眠:普通是挪用Thread.sleep後的狀況,這時候線程仍然持有運轉所需的各類資本,然則不會被CPU調劑。
    掛起:普通是挪用Thread.suspend後的狀況,和休眠相似,CPU不會調劑該線程,分歧的是,這類狀況下,線程會釋放一切資本。
    逝世亡:線程運轉停止或許挪用了Thread.stop辦法。

上面我們來演示若何停止線程狀況切換,起首我們會用到上面辦法:

    Thread()或許Thread(Runnable):結構線程。
    Thread.start:啟動線程。
    Thread.sleep:將線程切換至休眠狀況。
    Thread.interrupt:中止線程的履行。
    Thread.join:期待某線程停止。
    Thread.yield:褫奪線程在CPU上的履行時光片,期待下一次調劑。
    Object.wait:將Object上一切線程鎖定,直到notify辦法才持續運轉。
    Object.notify:隨機叫醒Object上的1個線程。
    Object.notifyAll:叫醒Object上的一切線程。

上面,就是演示時光啦!!!

線程期待與叫醒

這裡重要應用Object.wait和Object.notify辦法,請拜見下面的notify實例。須要留意的是,wait和notify都必需針對統一個對象,當我們應用完成Runnable接口的方法來創立線程時,應當是在Runnable對象而非Thread對象上應用這兩個辦法。

線程的休眠與叫醒

Thread.sleep實例
 public class SleepSample {

     public static void main(String[] args) throws InterruptedException
     {
         sleepTest();
     }

     private static void sleepTest() throws InterruptedException
     {
         Thread thread = new Thread()
         {
             public void run()
             {
                 System.out.println("線程 " + Thread.currentThread().getName() + "將要休眠5分鐘。");
                 try
                 {
                     Thread.sleep(5*60*1000);
                 }
                 catch(InterruptedException ex)
                 {
                     System.out.println("線程 " + Thread.currentThread().getName() + "休眠被中止。");
                 }
                 System.out.println("線程 " + Thread.currentThread().getName() + "休眠停止。");
             }
         };
         thread.setDaemon(true);
         thread.start();
         Thread.sleep(500);
         thread.interrupt();
     }

 }

線程在休眠進程中,我們可使用Thread.interrupt將其叫醒,這時候線程會拋出InterruptedException。

線程的終止

固然有Thread.stop辦法,但該辦法是不被推舉應用的,我們可以應用下面休眠與叫醒的機制,讓線程在處置IterruptedException時,停止線程。

Thread.interrupt示例
 public class StopThreadSample {

     public static void main(String[] args) throws InterruptedException
     {
         stopTest();
     }

     private static void stopTest() throws InterruptedException
     {
         Thread thread = new Thread()
         {
             public void run()
             {
                 System.out.println("線程運轉中。");
                 try
                 {
                     Thread.sleep(1*60*1000);
                 }
                 catch(InterruptedException ex)
                 {
                     System.out.println("線程中止,停止線程");
                     return;
                 }
                 System.out.println("線程正常停止。");
             }
         };
         thread.start();
         Thread.sleep(500);
         thread.interrupt();
     }
 }

線程的同步期待

當我們在主線程中創立了10個子線程,然後我們希冀10個子線程全體停止後,主線程在履行接上去的邏輯,這時候,就該Thread.join退場了。

Thread.join示例
 public class JoinSample {

     public static void main(String[] args) throws InterruptedException
     {
         joinTest();
     }

     private static void joinTest() throws InterruptedException
     {
         Thread thread = new Thread()
         {
             public void run()
             {
                 try
                 {
                     for(int i = 0; i < 5; i++)
                     {
                         System.out.println("線程在運轉。");
                         Thread.sleep(1000);
                     }
                 }
                 catch(InterruptedException ex)
                 {
                     ex.printStackTrace();
                 }
             }
         };
         thread.setDaemon(true);
         thread.start();
         Thread.sleep(1000);
         thread.join();
         System.out.println("主線程正常停止。");
     }
 }

我們可以試著將thread.join();正文或許刪除,再次運轉法式,便可以發明分歧了。

線程間通訊

我們曉得,一個過程上面的一切線程是同享內存空間的,那末我們若何在分歧的線程之間傳遞新聞呢?在回想 Java I/O時,我們談到了PipedStream和PipedReader,這裡,就是它們施展感化的處所了。

上面的兩個示例,功效完整一樣,分歧的是一個應用Stream,一個應用Reader/Writer。

PipeInputStream/PipedOutpueStream 示例
 public static void communicationTest() throws IOException, InterruptedException
 {
     final PipedOutputStream pos = new PipedOutputStream();
     final PipedInputStream pis = new PipedInputStream(pos);

     Thread thread1 = new Thread()
     {
         public void run()
         {
             BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
             try
             {
                 while(true)
                 {
                     String message = br.readLine();
                     pos.write(message.getBytes());
                     if (message.equals("end")) break;
                 }
                 br.close();
                 pos.close();
             }
             catch(Exception ex)
             {
                 ex.printStackTrace();
             }
         }
     };

     Thread thread2 = new Thread()
     {
         public void run()
         {
             byte[] buffer = new byte[1024];
             int bytesRead = 0;
             try
             {
                 while((bytesRead = pis.read(buffer, 0, buffer.length)) != -1)
                 {
                     System.out.println(new String(buffer));
                     if (new String(buffer).equals("end")) break;
                     buffer = null;
                     buffer = new byte[1024];
                 }
                 pis.close();
                 buffer = null;
             }
             catch(Exception ex)
             {
                 ex.printStackTrace();
             }
         }
     };

     thread1.setDaemon(true);
     thread2.setDaemon(true);
     thread1.start();
     thread2.start();
     thread1.join();
     thread2.join();
 }


PipedReader/PipedWriter 示例
 private static void communicationTest2() throws InterruptedException, IOException
 {
     final PipedWriter pw = new PipedWriter();
     final PipedReader pr = new PipedReader(pw);
     final BufferedWriter bw = new BufferedWriter(pw);
     final BufferedReader br = new BufferedReader(pr);

     Thread thread1 = new Thread()
     {
         public void run()
         {

             BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
             try
             {
                 while(true)
                 {
                     String message = br.readLine();
                     bw.write(message);
                     bw.newLine();
                     bw.flush();
                     if (message.equals("end")) break;
                 }
                 br.close();
                 pw.close();
                 bw.close();
             }
             catch(Exception ex)
             {
                 ex.printStackTrace();
             }
         }
     };

     Thread thread2 = new Thread()
     {
         public void run()
         {

             String line = null;
             try
             {
                 while((line = br.readLine()) != null)
                 {
                     System.out.println(line);
                     if (line.equals("end")) break;
                 }
                 br.close();
                 pr.close();
             }
             catch(Exception ex)
             {
                 ex.printStackTrace();
             }
         }
     };

     thread1.setDaemon(true);
     thread2.setDaemon(true);
     thread1.start();
     thread2.start();
     thread1.join();
     thread2.join();
 }

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved