位置:首页 > Java技术 > Java基础教程 > Java多线程

Java多线程

Java提供了对多线程编程的内置支持。多线程程序中包含可以同时运行两个或多个部分。这样一个程序的每个部分称为一个线程,每个线程定义一个单独的执行路径。

多线程是多任务的一种特殊形式。多线程需要比多任务处理开销更少。

需要定义其他术语相关:线程进程。进程包括操作系统,它可以包含一个或多个线程分配的内存空间。线程不能单独存在,它必须是一个进程的一部分。一个进程仍然在运行,直到所有的非守护线程都执行完毕。

多线程能够编写非常高效的程序,最大限度地利用CPU,因为空闲时间可以保持在最低限度。

线程的生命周期:

一个线程经历在其生命周期的不同阶段。例如,一个线程产生,开始,运行,然后死亡。下图显示了一个线程的整个生命周期。

Java Thread

上述阶段进行了说明:

  • New: 一个新的线程开始在新的状态的生命周期。它仍然在此状态,直到程序启动的线程。它也被称为出生线程。

  • Runnable: 经过一个新生的线程启动时,该线程进入可运行状态。处于这种状态的线程将被视为执行其任务。

  • Waiting: 有时候,一个线程转换到等待状态,而线程等待另一个线程来执行任务。一个线程转换回可运行状态,只有当另一个线程发出信号的等待线程继续执行。

  • Timed waiting: 可运行的线程可以输入指定的时间间隔定时等待状态。处于这种状态的线程转换回可运行状态时的时间间隔期满时或在等待事件发生。

  • Terminated: 可运行的线程进入终止状态,当它完成了自己的任务,否则终止。

线程的优先级:

每一个Java线程都有一个优先级,可以帮助操作系统决定哪个线程预定的顺序。

Java优先级是在MIN_PRIORITY(常数1)和MAX_PRIORITY(常数10)之间的范围内。默认情况下,每个线程优先NORM_PRIORITY(常数5)。

线程具有更高的优先级是比较重要的一个程序和低优先级的线程之前应该分配处理器时间。但是,线程优先级不能保证哪个线程的执行顺序和非常依赖平台。

创建线程:

Java定义在此可以完成两种方式:

  • 可以实现Runnable接口。

  • 可以扩展Thread类本身。

通过实现Runnable创建线程:

创建一个线程,最简单的方法是创建一个实现Runnable接口的类。

为了实现Runnable,这个类需要实现只有一个单一的方法 run(),它是这样声明的:

public void run( )

定义构成新线程 run()方法的代码内部。重要的是要明白的run()可以调用其他方法,使用其他类,并声明变量,就像主线程可以是很重要的。 

当创建一个实现Runnable类,会从类中实例化线程的对象。线程定义了多个构造函数。我们将使用一个如下所示:

Thread(Runnable threadOb, String threadName);

在这里,threadOb是实现Runnable接口和新线程的名称是由threadName指定一个类的实例。

创建新线程后,它不会启动运行,直到调用它的start()方法,它是内线程声明。start()方法如下所示:

void start( );

例子:

下面是创建一个新的线程并开始运行一个例子:

// Create a new thread.
class NewThread implements Runnable {
   Thread t;
   NewThread() {
      // Create a new, second thread
      t = new Thread(this, "Demo Thread");
      System.out.println("Child thread: " + t);
      t.start(); // Start the thread
   }
   
   // This is the entry point for the second thread.
   public void run() {
      try {
         for(int i = 5; i > 0; i--) {
            System.out.println("Child Thread: " + i);
            // Let the thread sleep for a while.
            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(); // create a new thread
      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.");
   }
}

这将产生以下结果:

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.

通过扩展Thread创建线程:

创建一个线程的第二种方式是创建可扩展条条一个新的类,然后创建一个类的实例。

扩展类必须重写run()方法,这是切入点的新线程。它还必须调用start()开始执行新线程。

例子:

下面是重写扩展线程前面的程序:

// Create a second thread by extending Thread
class NewThread extends Thread {
   NewThread() {
      // Create a new, second thread
      super("Demo Thread");
      System.out.println("Child thread: " + this);
      start(); // Start the thread
   }

   // This is the entry point for the second thread.
   public void run() {
      try {
         for(int i = 5; i > 0; i--) {
            System.out.println("Child Thread: " + i);
			// Let the thread sleep for a while.
            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(); // create a new thread
      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.");
   }
}

这将产生以下结果:

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.

线程方法:

以下是在Thread类提供重要的方法列表。

SN 方法及描述
1 public void start()
Starts the thread in a separate path of execution, then invokes the run() method on this Thread object.
2 public void run()
If this Thread object was instantiated using a separate Runnable target, the run() method is invoked on that Runnable object.
3 public final void setName(String name)
Changes the name of the Thread object. There is also a getName() method for retrieving the name.
4 public final void setPriority(int priority)
Sets the priority of this Thread object. The possible values are between 1 and 10.
5 public final void setDaemon(boolean on)
A parameter of true denotes this Thread as a daemon thread.
6 public final void join(long millisec)
The current thread invokes this method on a second thread, causing the current thread to block until the second thread terminates or the specified number of milliseconds passes.
7 public void interrupt()
Interrupts this thread, causing it to continue execution if it was blocked for any reason.
8 public final boolean isAlive()
Returns true if the thread is alive, which is any time after the thread has been started but before it runs to completion.

以前的方法是在一个特定的Thread对象调用。在Thread类下面的方法都是静态的。调用静态方法之一执行当前正在运行的线程上运行。

SN 方法及描述
1 public static void yield()
Causes the currently running thread to yield to any other threads of the same priority that are waiting to be scheduled.
2 public static void sleep(long millisec)
Causes the currently running thread to block for at least the specified number of milliseconds.
3 public static boolean holdsLock(Object x)
Returns true if the current thread holds the lock on the given Object.
4 public static Thread currentThread()
Returns a reference to the currently running thread, which is the thread that invokes this method.
5 public static void dumpStack()
Prints the stack trace for the currently running thread, which is useful when debugging a multithreaded application.

例子:

下面ThreadClassDemo程序演示了一些Thread类的这些方法。考虑类DisplayMessage它实现了Runnable:

// File Name : DisplayMessage.java
// Create a thread to implement 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);
      }
   }
}

下面是另一个类,它扩展了Thread类:

// File Name : GuessANumber.java
// Create a thread to extentd Thread -by www.gitbook.net
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.**");
   }
}

以下是主要的程序,利用了上述定义的类:

// File Name : 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...");
   }
}

这将产生以下结果。可以试试这个例子多次,每次都会得到不同的结果。

Starting hello thread...
Starting goodbye thread...
Hello
Hello
Hello
Hello
Hello
Hello
Goodbye
Goodbye
Goodbye
Goodbye
Goodbye
.......

主要线程概念:

多线程编程,需要具备以下概念非常必要:

使用多线程:

有效地利用多线程支持的关键是同时,而非串行。例如,当有一个程序,可以并发执行中两个子系统,使它们各个线程运行。

只要仔细地使用多线程,可以创建非常高效的程序。要注意的是:如果你创建了太多的线程,可以实际降低程序的性能,而不是提高它。

请记住,一些开销与上下文切换有关。如果创建了太多的线程,更多的CPU时间会花在不断变化的环境不是执行程序!