pengaturcaraan berbilang benang Java


Java menyediakan sokongan terbina dalam untuk pengaturcaraan berbilang benang. Program berbilang benang terdiri daripada dua atau lebih bahagian yang boleh dijalankan serentak. Setiap bahagian program dipanggil thread, dan setiap thread mentakrifkan laluan pelaksanaan bebas.

Berbilang benang ialah satu bentuk khas berbilang tugas, tetapi berbilang benang menggunakan overhed sumber yang lebih kecil.

Di sini mentakrifkan istilah lain yang berkaitan dengan utas - proses: Proses termasuk ruang memori yang diperuntukkan oleh sistem pengendalian dan mengandungi satu atau lebih utas. Benang tidak boleh wujud secara bebas, ia mesti menjadi sebahagian daripada proses. Satu proses berjalan sehingga semua utas yang tidak menunggu selesai dijalankan.

Multi-threading membolehkan pengaturcara menulis program yang cekap untuk menggunakan CPU sepenuhnya.


Kitaran hayat benang

Sebuah benang melalui pelbagai peringkat kitaran hayatnya. Rajah berikut menunjukkan kitaran hayat lengkap benang.

java-thread.jpg

  • Status baharu:

    Gunakan baharu kata kunci dan Selepas kelas Thread atau subkelasnya mencipta objek thread, objek thread berada dalam keadaan yang baru dibuat. Ia kekal dalam keadaan ini sehingga program start() thread ini.

  • Keadaan sedia:

    Apabila objek benang memanggil kaedah mula(), benang memasuki keadaan sedia. Benang dalam keadaan sedia berada dalam baris gilir sedia dan sedang menunggu penjadualan oleh penjadual benang dalam JVM.

  • Status berjalan:

    Jika benang dalam keadaan sedia memperoleh sumber CPU, ia boleh melaksanakan run() , ini Apabila benang sedang berjalan. Benang dalam keadaan berjalan adalah yang paling kompleks, ia boleh menjadi tersekat, sedia dan mati.

  • Status penyekatan:

    Jika thread melaksanakan sleep, suspend dan kaedah lain, selepas kehilangan sumber yang diduduki , thread memasuki keadaan menyekat daripada keadaan berjalan. Keadaan sedia boleh dimasukkan semula selepas masa tidur tamat atau sumber peranti telah diperolehi.

  • Keadaan mati:

    Apabila benang berjalan menyelesaikan tugasnya atau keadaan penamatan lain berlaku, benang bertukar kepada keadaan ditamatkan.


Keutamaan utas

Setiap utas Java mempunyai keutamaan, yang membantu sistem pengendalian menentukan susunan penjadualan urutan.

Keutamaan benang Java ialah integer dan julat nilainya ialah 1 (Thread.MIN_PRIORITY) - 10 (Thread.MAX_PRIORITY).

Secara lalai, setiap urutan diberikan tahap keutamaan NORM_PRIORITY (5).

Thread dengan keutamaan yang lebih tinggi adalah lebih penting kepada program dan harus diperuntukkan sumber pemproses sebelum thread dengan keutamaan yang lebih rendah. Walau bagaimanapun, keutamaan thread tidak menjamin susunan pelaksanaan thread dan sangat bergantung pada platform.


Buat utas

Java menyediakan dua kaedah untuk mencipta utas:

  • Dengan melaksanakan antara muka Boleh Dijalankan

  • Dengan mewarisi kelas Thread itu sendiri.


Buat urutan dengan melaksanakan antara muka Runnable

Untuk mencipta thread, cara paling mudah ialah mencipta kelas yang melaksanakan antara muka Runnable.

Untuk melaksanakan Runnable, kelas hanya perlu melaksanakan kaedah panggilan run(), diisytiharkan seperti berikut:


public void run()

Anda boleh mengatasi kaedah ini , penting Difahamkan bahawa run() boleh memanggil kaedah lain, menggunakan kelas lain dan mengisytiharkan pembolehubah, sama seperti utas utama.

Selepas mencipta kelas yang melaksanakan antara muka Runnable, anda boleh membuat instantiate objek thread dalam kelas.

Thread mentakrifkan beberapa pembina, yang berikut ialah perkara yang sering kita gunakan:

Thread(Runnable threadOb,String threadName);

Di sini, threadOb ialah contoh kelas yang melaksanakan antara muka Runnable, dan threadName menentukan nama yang baharu benang.

Selepas urutan baharu dicipta, ia tidak akan dijalankan sehingga anda memanggil kaedah mula()nya.

void start();

Contoh

Berikut ialah contoh mencipta utas dan memulakannya untuk melaksanakan:

// 创建一个新的线程
class NewThread implements Runnable {
   Thread t;
   NewThread() {
      // 创建第二个新线程
      t = new Thread(this, "Demo Thread");
      System.out.println("Child thread: " + t);
      t.start(); // 开始线程
   }
  
   // 第二个线程入口
   public void run() {
      try {
         for(int i = 5; i > 0; i--) {
            System.out.println("Child Thread: " + i);
            // 暂停线程
            Thread.sleep(50);
         }
     } catch (InterruptedException e) {
         System.out.println("Child interrupted.");
     }
     System.out.println("Exiting child thread.");
   }
}
 
public class ThreadDemo {
   public static void main(String args[]) {
      new NewThread(); // 创建一个新线程
      try {
         for(int i = 5; i > 0; i--) {
           System.out.println("Main Thread: " + i);
           Thread.sleep(100);
         }
      } catch (InterruptedException e) {
         System.out.println("Main thread interrupted.");
      }
      System.out.println("Main thread exiting.");
   }
}

Kompilasi atur cara di atas dan jalankan hasilnya seperti berikut:

Child thread: Thread[Demo Thread,5,main]
Main Thread: 5
Child Thread: 5
Child Thread: 4
Main Thread: 4
Child Thread: 3
Child Thread: 2
Main Thread: 3
Child Thread: 1
Exiting child thread.
Main Thread: 2
Main Thread: 1
Main thread exiting.

Buat utas dengan mewarisi Thread

Cara kedua untuk mencipta utas ialah mencipta kelas baharu yang mewarisi kelas Thread, dan kemudian mencipta tika kelas itu.

Kelas yang diwarisi mesti mengatasi kaedah run(), yang merupakan titik masuk urutan baharu. Ia juga mesti memanggil kaedah mula() untuk dilaksanakan.

Contoh

// 通过继承 Thread 创建线程
class NewThread extends Thread {
   NewThread() {
      // 创建第二个新线程
      super("Demo Thread");
      System.out.println("Child thread: " + this);
      start(); // 开始线程
   }
 
   // 第二个线程入口
   public void run() {
      try {
         for(int i = 5; i > 0; i--) {
            System.out.println("Child Thread: " + i);
                            // 让线程休眠一会
            Thread.sleep(50);
         }
      } catch (InterruptedException e) {
         System.out.println("Child interrupted.");
      }
      System.out.println("Exiting child thread.");
   }
}
 
public class ExtendThread {
   public static void main(String args[]) {
      new NewThread(); // 创建一个新线程
      try {
         for(int i = 5; i > 0; i--) {
            System.out.println("Main Thread: " + i);
            Thread.sleep(100);
         }
      } catch (InterruptedException e) {
         System.out.println("Main thread interrupted.");
      }
      System.out.println("Main thread exiting.");
   }
}

Kompilasikan atur cara di atas dan jalankan hasilnya seperti berikut:

Child thread: Thread[Demo Thread,5,main]
Main Thread: 5
Child Thread: 5
Child Thread: 4
Main Thread: 4
Child Thread: 3
Child Thread: 2
Main Thread: 3
Child Thread: 1
Exiting child thread.
Main Thread: 2
Main Thread: 1
Main thread exiting.

Kaedah benang

Jadual berikut menyenaraikan beberapa kaedah penting daripada kelas Thread :

序号方法描述
                    1public void start()
使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
                    2public void run()
如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。
                    3public final void setName(String name)
改变线程名称,使之与参数 name 相同。
                    4public final void setPriority(int priority)
 更改线程的优先级。
                    5public final void setDaemon(boolean on)
将该线程标记为守护线程或用户线程。
                    6public final void join(long millisec)
等待该线程终止的时间最长为 millis 毫秒。
                    7public void interrupt()
中断线程。
                    8public final boolean isAlive()
测试线程是否处于活动状态。

Uji sama ada rangkaian itu aktif. Kaedah di atas dipanggil oleh objek Thread. Kaedah berikut adalah kaedah statik kelas Thread.

序号方法描述
                    1public static void yield()
暂停当前正在执行的线程对象,并执行其他线程。
                    2public static void sleep(long millisec)
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
                    3public static boolean holdsLock(Object x)
当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。
                    4public static Thread currentThread()
返回对当前正在执行的线程对象的引用。
                    5public static void dumpStack()
将当前线程的堆栈跟踪打印至标准错误流。

Contoh

Program ThreadClassDemo berikut menunjukkan beberapa kaedah kelas Thread:

// 文件名 : DisplayMessage.java
// 通过实现 Runnable 接口创建线程
public class DisplayMessage implements Runnable
{
   private String message;
   public DisplayMessage(String message)
   {
      this.message = message;
   }
   public void run()
   {
      while(true)
      {
         System.out.println(message);
      }
   }
}
// 文件名 : GuessANumber.java
// 通过继承 Thread 类创建线程

public class GuessANumber extends Thread
{
   private int number;
   public GuessANumber(int number)
   {
      this.number = number;
   }
   public void run()
   {
      int counter = 0;
      int guess = 0;
      do
      {
          guess = (int) (Math.random() * 100 + 1);
          System.out.println(this.getName()
                       + " guesses " + guess);
          counter++;
      }while(guess != number);
      System.out.println("** Correct! " + this.getName()
                       + " in " + counter + " guesses.**");
   }
}
// 文件名 : ThreadClassDemo.java
public class ThreadClassDemo
{
   public static void main(String [] args)
   {
      Runnable hello = new DisplayMessage("Hello");
      Thread thread1 = new Thread(hello);
      thread1.setDaemon(true);
      thread1.setName("hello");
      System.out.println("Starting hello thread...");
      thread1.start();
     
      Runnable bye = new DisplayMessage("Goodbye");
      Thread thread2 = new Thread(bye);
      thread2.setPriority(Thread.MIN_PRIORITY);
      thread2.setDaemon(true);
      System.out.println("Starting goodbye thread...");
      thread2.start();
 
      System.out.println("Starting thread3...");
      Thread thread3 = new GuessANumber(27);
      thread3.start();
      try
      {
         thread3.join();
      }catch(InterruptedException e)
      {
         System.out.println("Thread interrupted.");
      }
      System.out.println("Starting thread4...");
      Thread thread4 = new GuessANumber(75);
     
           thread4.start();
      System.out.println("main() is ending...");
   }
}

Hasil larian adalah seperti berikut, dan keputusan setiap larian adalah berbeza .

Starting hello thread...
Starting goodbye thread...
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Thread-2 guesses 27
Hello
** Correct! Thread-2 in 102 guesses.**
Hello
Starting thread4...
Hello
Hello
..........remaining result produced.

Beberapa konsep utama utas:

Dalam pengaturcaraan berbilang benang, anda perlu memahami konsep berikut:

  • Penyegerakan benang

  • Komunikasi antara benang

  • Kebuntuan benang

  • Kawalan benang: gantung, hentikan dan sambung semula


Penggunaan multi-threading

Kunci kepada penggunaan multi-threading yang berkesan adalah untuk memahami bahawa program dilaksanakan secara serentak dan bukannya secara bersiri. Sebagai contoh: Terdapat dua subsistem dalam atur cara yang perlu dilaksanakan secara serentak Dalam kes ini, pengaturcaraan berbilang benang perlu digunakan.

Dengan menggunakan multi-threading, anda boleh menulis program yang sangat cekap. Walau bagaimanapun, sila ambil perhatian bahawa jika anda mencipta terlalu banyak utas, kecekapan pelaksanaan program sebenarnya akan dikurangkan, bukan dipertingkatkan.

Sila ingat bahawa overhed penukaran konteks juga penting Jika anda membuat terlalu banyak urutan, CPU akan menghabiskan lebih banyak masa untuk menukar konteks daripada melaksanakan program.