Java多线程实现方式主要有四种:继承Thread类、实现Runnable接口、实现Callable接口通过FutureTask包装器来创建Thread线程、使用ExecutorService、Callable、Future实现有返回结果的多线程。
其中前两种方式线程执行完后都没有返回值,后两种是带返回值的。

1、继承Thread类创建线程

Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。

  1. public class MyThread extends Thread {
  2. public void run() {
  3. System.out.println("MyThread.run()");
  4.  }
  5. }
  6. MyThread myThread1 = new MyThread();
  7. MyThread myThread2 = new MyThread();
  8. myThread1.start();
  9. myThread2.start();

2、实现Runnable接口创建线程

如果自己的类已经extends另一个类,就无法直接extends Thread,此时,可以实现一个Runnable接口,如下:

  1. public class Thread implements Runnable {
  2. public Thread(Runnable target) {
  3. init(null, target, "Thread-" + nextThreadNum(), 0);
  4. }
  5. Thread(Runnable target, AccessControlContext acc) {
  6. init(null, target, "Thread-" + nextThreadNum(), 0, acc, false);
  7. }
  8. private void init(ThreadGroup g, Runnable target, String name, long stackSize) {
  9. init(g, target, name, stackSize, null, true);
  10. }
  11. private void init(ThreadGroup g, Runnable target, String name, long stackSize,
  12. AccessControlContext acc, boolean inheritThreadLocals) {
  13. if (name == null) {
  14. throw new NullPointerException("name cannot be null");
  15. }
  16. this.name = name;
  17. Thread parent = currentThread();
  18. SecurityManager security = System.getSecurityManager();
  19. if (g == null) {
  20. /* Determine if it's an applet or not */
  21. /* If there is a security manager, ask the security manager what to do. */
  22. if (security != null) {
  23. g = security.getThreadGroup();
  24. }
  25. /* If the security doesn't have a strong opinion of the matter
  26. use the parent thread group. */
  27. if (g == null) {
  28. g = parent.getThreadGroup();
  29. }
  30. }
  31. /* checkAccess regardless of whether or not threadgroup is explicitly passed in. */
  32. g.checkAccess();
  33. /*
  34. * Do we have the required permissions?
  35. */
  36. if (security != null) {
  37. if (isCCLOverridden(getClass())) {
  38. security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
  39. }
  40. }
  41. g.addUnstarted();
  42. this.group = g;
  43. this.daemon = parent.isDaemon();
  44. this.priority = parent.getPriority();
  45. if (security == null || isCCLOverridden(parent.getClass()))
  46. this.contextClassLoader = parent.getContextClassLoader();
  47. else
  48. this.contextClassLoader = parent.contextClassLoader;
  49. this.inheritedAccessControlContext =
  50. acc != null ? acc : AccessController.getContext();
  51. this.target = target;
  52. setPriority(priority);
  53. if (inheritThreadLocals && parent.inheritableThreadLocals != null)
  54. this.inheritableThreadLocals =
  55. ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
  56. /* Stash the specified stack size in case the VM cares */
  57. this.stackSize = stackSize;
  58. /* Set thread ID */
  59. tid = nextThreadID();
  60. }
  61. }
  62. @FunctionalInterface //表名是函数式接口
  63. public interface Runnable {
  64. public abstract void run();
  65. }
  66. public class MyThread extends OtherClass implements Runnable {
  67.  public void run() {
  68. System.out.println("MyThread.run()");
  69.  }
  70. }
  71. //为了启动MyThread,需要首先实例化一个Thread,并传入自己的MyThread实例:
  72. MyThread myThread = new MyThread();
  73. Thread thread = new Thread(myThread);
  74. thread.start();
  75. //事实上,当传入一个Runnable target参数给Thread后,Thread的run()方法就会调用target.run(),参考JDK源代码:
  76. public void run() {
  77.  if (target != null) {
  78. target.run();
  79.  }
  80. }

3、实现Callable接口通过FutureTask包装器来创建Thread线程

Callable接口(也只有一个方法)定义如下:

  1. public interface Callable {
  2. V call() throws Exception;
  3. }
  4. public class SomeCallable extends OtherClass implements Callable {
  5. Override
  6. public V call() throws Exception {
  7. // TODO Auto-generated method stub
  8. return null;
  9. }
  10. }
  11. Callable oneCallable = new SomeCallable();
  12. //由Callable创建一个FutureTask对象:
  13. FutureTask oneTask = new FutureTask(oneCallable);
  14. //注释:FutureTask是一个包装器,它通过接受Callable来创建,它同时实现了Future和Runnable接口。
  15. //由FutureTask创建一个Thread对象:
  16. Thread oneThread = new Thread(oneTask);
  17. oneThread.start();

4、使用ExecutorService、Callable、Future实现有返回结果的线程

ExecutorService、Callable、Future三个接口实际上都是属于Executor框架。返回结果的线程是在JDK1.5中引入的新特征,有了这种特征就不需要再为了得到返回值而大费周折了。而且自己实现了也可能漏洞百出。
可返回值的任务必须实现Callable接口。类似的,无返回值的任务必须实现Runnable接口。
执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了。
注意:get方法是阻塞的,即:线程无返回结果,get方法会一直等待。再结合线程池接口ExecutorService就可以实现传说中有返回结果的多线程了。
下面提供了一个完整的有返回结果的多线程测试例子,在JDK1.5下验证过没问题可以直接使用。代码如下:

  1. import java.util.concurrent.*;
  2. import java.util.Date;
  3. import java.util.List;
  4. import java.util.ArrayList;
  5. /**
  6. 有返回值的线程
  7. */
  8. @SuppressWarnings("unchecked")
  9. public class Test {
  10. public static void main(String[] args) throws ExecutionException, InterruptedException {
  11. System.out.println("----程序开始运行----");
  12. Date date1 = new Date();
  13. int taskSize = 5;
  14. // 创建一个线程池
  15. ExecutorService pool = Executors.newFixedThreadPool(taskSize);
  16. // 创建多个有返回值的任务
  17. List list = new ArrayList();
  18. for (int i = 0; i < taskSize; i++) {
  19. Callable c = new MyCallable(i + " ");
  20. // 执行任务并获取Future对象
  21. Future f = pool.submit(c);
  22. // System.out.println(">>>" + f.get().toString());
  23. list.add(f);
  24. }
  25. // 关闭线程池
  26. pool.shutdown();
  27. // 获取所有并发任务的运行结果
  28. for (Future f : list) {
  29. // 从Future对象上获取任务的返回值,并输出到控制台
  30. System.out.println(">>>" + f.get().toString());
  31. }
  32. Date date2 = new Date();
  33. System.out.println("----程序结束运行----,程序运行时间【"
  34. (date2.getTime() - date1.getTime()) + "毫秒】");
  35. }
  36. }
  37. class MyCallable implements Callable
  38. MyCallable(String taskNum) {
  39. this.taskNum = taskNum;
  40. }
  41. public Object call() throws Exception {
  42. System.out.println(">>>" + taskNum + "任务启动");
  43. Date dateTmp1 = new Date();
  44. Thread.sleep(1000);
  45. Date dateTmp2 = new Date();
  46. long time = dateTmp2.getTime() - dateTmp1.getTime();
  47. System.out.println(">>>" + taskNum + "任务终止");
  48. return taskNum + "任务返回运行结果,当前任务时间【" + time + "毫秒】";
  49. }
  50. }

代码说明:
上述代码中Executors类,提供了一系列工厂方法用于创建线程池,返回的线程池都实现了ExecutorService接口。
public static ExecutorService newFixedThreadPool(int nThreads)
创建固定数目线程的线程池。
public static ExecutorService newCachedThreadPool()
创建一个可缓存的线程池,调用execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。
public static ExecutorService newSingleThreadExecutor()
创建一个单线程化的Executor。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。
ExecutoreService提供了submit()方法,传递一个Callable,或Runnable,返回Future。如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。

线程5种状态

  1. 新建状态(New) : 线程对象被创建后,就进入了新建状态。例如,Thread thread = new Thread()。
  2. 就绪状态(Runnable): 也被称为“可执行状态”。线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。例如,thread.start()。处于就绪状态的线程,随时可能被CPU调度执行。
  3. 运行状态(Running) : 线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。
  4. 阻塞状态(Blocked) : 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:

(01) 等待阻塞 — 通过调用线程的wait()方法,让线程等待某工作的完成。
(02) 同步阻塞 — 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。
(03) 其他阻塞 — 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

  1. 死亡状态(Dead) : 线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

说明:
这5种状态涉及到的内容包括Object类, Thread和synchronized关键字。这些内容我们会在后面的章节中逐个进行学习。
Object类,定义了wait(), notify(), notifyAll()等休眠/唤醒函数。
Thread类,定义了一些列的线程操作函数。例如,sleep()休眠函数, interrupt()中断函数, getName()获取线程名称等。
synchronized,是关键字;它区分为synchronized代码块和synchronized方法。synchronized的作用是让线程获取对象的同步锁。
在后面详细介绍wait(),notify()等方法时,我们会分析为什么“wait(), notify()等方法要定义在Object类,而不是Thread类中”。