线程的创建与启动

357 阅读3分钟

Java使用Thread类代表线程,所有的线程对象都必须是Tread类或其子类的实例。

1 继承Thread类创建线程类

  1. 定义Thread类的子类,并重写run()方法,该run()方法代表了线程需要完成的任务,因此run()方法称为线程执行体;
  2. 创建子类的实例,即创建了线程对象;
  3. 通过调用start()方法启动线程,执行run()方法。

当java程序启动后,程序至少会创建一个主线程,主线程的方法体不是有run()方法决定的,而是有main()方法确定的,main()方法的方法体代表主线程的线程执行体。

2 实现Runnable接口创建线程类

  1. 定义实现Runnable接口的类,并重写该接口的run()方法,该方法同样是该线程的线程执行体;
  2. 创建实现了Runnable接口类的实例对象,并将该对象作为Thread类的target(参数)来创建对象,该Thread对象才是真正的线程对象。
  3. 调用线程对象的start()方法来启动该线程。
// 创建实现了Runnable类的对象
RunnableThread st = new RunnableThread();
// 将该对象作为参数创建一个Thread类的对象
new Thread(st);
// 也可以在创建对象的时候指定新的线程名,这里用st创建了两个线程对象。他们共享RunnableThread中的实例变量
new Thread(st,"新线程1");
new Thread(st,"新线程2");

Runnable对象仅仅作为Thread对象的target,Runnable实现类中重写的run()方法仅作为线程的执行体。而实际的线程对象依然是Thread实例,该Thread线程负责执行target的run()方法。

3 使用Callable和Future创建线线程

  1. 创建Callable的实现类,并实现call()方法,该call()方法将作为线程执行体,且该call()方法有返回值,在创建Callable实现类的实例;
  2. 使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值;
  3. 使用FutureTask对象作为Thread的target创建并启动新线程;
  4. 调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。 实现Callable接口与实现Runnable接口的方式节本相同,只是Callable中的call()方法有返回值,可以抛出异常。

对比

使用Runnable和Callable实现:

  • 优点:线程类只是实现了接口,还可以继承其他类;多个线程可以共享同一个实现了接口的对象,适合多个相同的线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好的体现了面向对象的思想。
  • 缺点:编程稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread()方法

继承Thread类创建线程的方法:

  • 优点:编写简单,可以直接使用this访问当前线程;
  • 缺点:已经继承了Thread,不能再继承其他父类。

一般推荐采用实现Runnable、Callable接口的方法来创建多线程。