一、Java中线程实现
Java 中实现多线程的代码有三种方式,一种是继承 Thread 类,另一种是实现 Runnable 接口,在JDK1.5之后还有一个 Callable 接口,Runnable 接口方式利于资源共享的处理,Callable 接口的实现方式可以获取线程的返回值。
1. 方法1——继承 Thread 类
Thread 类是在 java.lang 包中定义的。一个类只要继承了 Thread 类就称为多线程操作类。在 Thread 的子类中必须明确覆写 Thread 类中的 run() 方法,此方法为线程主体。线程类定义如下:
class 类名 extends Thread { 属性... 方法... public void run() { 线程主体 } }
启动线程是调用 Thread 类的 start() 方法,而不是 run() 方法。若直接调用 run() 方法就是一个普通方法调用,而不是多线程。并且 start() 方法只能调用一次,因为 start() 方法中有一个调用计数,多次调用会 throw new IllegalThreadStateException() 异常。
例子:
class MyThread extends Thread { private String name; public MyThread(String name) { this.name = name; } public void run() { for (int i = 0; i < 10; i++) { System.out.println("name: " + name + " i=" + i); } } } public class ThreadDemo { public static void main(String args[]) { MyThread mt1 = new MyThread("mt1"); MyThread mt2 = new MyThread("mt2"); //mt1.run(); //简单的方法调用 //mt2.run(); mt1.start(); mt2.start(); //mt2.start(); //触发IllegalThreadStateException异常 } }
2. 方法2——实现 Runnable 接口
Java 中也可以通过实现 Runnable 接口的方式实现多线程,此接口定义为:
public interface Runnable { public void run(); }
使用 Runnable 接口实现多线程的格式:
class 类名 implements Runnable { 属性... 方法... public void run() { 线程主体 } }
Runnable 接口实际上还是依靠 Thread 实现多线程启动的,可以看 Thread 类的定义就知道使用方法了:
public class Thread extends Object implements Runnable { private Runnable target; public Thread(Runnable target, String name) { init(null, target, name, 0); } private void init(ThreadGroup g, Runnable target, String name, long stackSize) { ... this.target = target; ... } public void run() { if (target != null) { target.run(); } } }
如果传了 Runnable 类型的参数,最终执行的就是 Runnable 参数的 run() 方法。
举例1:
class MyThread implements Runnable { private String name; public MyThread(String name) { this.name = name; } public void run() { for (int i = 0; i < 10; i++) { System.out.println("name: " + name + " i=" + i); } } } public class ThreadDemo { public static void main(String args[]) { MyThread mt1 = new MyThread("mt1"); MyThread mt2 = new MyThread("mt2"); Thread t1 = new Thread(mt1); //传腹泻run()方法的Runnable的子类 Thread t2 = new Thread(mt2); t1.start(); t2.start(); } }
通过 Runnable 接口实现多线程比起通过实现 Thread 类实现多线程的优势是便于多个同类对象资源共享时的处理。因为后者的运行的 run() 方法是来自参数对象的,因此多个线程传同一个参数对象的话其属性就只有一份资源。而前者需要定义多个对象然后调用其 run() 方法实现多线程,由于是多个对象,其属性就是多个资源了。开发过程中建议使用 Runnable 接口的实现方式实现多线程。
3. 方法3——利用 Callable 接口
通过 Runnable 接口实现的多线程会出现 run() 方法不能返回操作结果的问题,为了解决此问题,JDK1.5开始提供了一个新的接口 java.util.concurrent.Callable,定义如下:
public interface Callable<V> { public V call() throws Exception; }
call() 方法在执行完后可以返回一个具体类型的数据。但是 Thread 类中没有定义任何构造方法来接收 Callable 接口对象实现对象,这导致多线程的启动又遇到了问题,JDK1.5之后开始提供一个 java.util.concurrent.FutureTask<V> 类来解决这个问题,其定义:
public class FutureTask<V> extends Object implements RunnableFuture<V>
FutureTask 实现了 RunnableFuture 接口,而后者又同时实现了 Future 和 Runnable 接口。如果想要接收线程执行的返回结果,调用 Future 接口中的 get() 方法即可。FutureTask 类常用方法如下:
public FutureTask(Callable<V> callable); //构造函数,接收 Callable 接口对象实例 public FutureTask(Runnable runnable, V result); //接收 Runnable 接口实例,并指定返回结果类型 public V get() throws InterruptedException, ExecutionException; //取得线程的执行结果,由 Future 接口定义
FutureTask 是 Runnable 接口的子类,并且其构造函数可以接收 Callable 实例,因此依然可以利用 Thread 类来实现多线程的启动。若想获取线程执行结果,则利用 Future 接口中的 get() 方法。
例子:
import java.util.concurrent.Callable; class MyThread implements Callable<String> { private int ticket = 5; @override public String call() throws Exception { for (int i = 0; i < 10; i++) { if (ticket > 0) { System.out.println("ticket left: " + ticket--); } } return "sold out"; } } public class ThreadDemo { public static void main(String args[]) { MyThread mt1 = new MyThread(); MyThread mt2 = new MyThread(); FutureTask<String> task1 = new FutureTask<String>(mt1); FutureTask<String> task2 = new FutureTask<String>(mt2); new Thread(task1).start(); new Thread(task2).start(); System.out.println("task1 return: " + task1.get()); System.out.println("task2 return: " + task2.get()); } }
但是报找不到 FutureTask 这个符号,可能是我本地 java 版本比较低。
Runnable 接口是 Java 最早提供也是使用最广泛的,平时建议通过使用 Runnable 接口的方式实现多线程。