Java 线程 2 - 线程的创建

13 篇文章 0 订阅
12 篇文章 2 订阅

参考:

Java 线程 0 - 前言


Java 共有 3 种方式用来创建线程,分别是

  1. 继承 Thread 类或其子类
  2. 实现接口 Runnable
  3. 实现接口 CallableFuture

主要内容:

  1. 线程构造器,run()start() 方法
  2. 继承 Thread
  3. 实现接口 Runnable
  4. 实现接口 CallableFuture
  5. 线程创建方式的比较

线程构造器, run()start() 方法

Java 使用 java.lang.Thread 类及其子类作为线程类,创建新线程,必须是 Thread 类的子类

线程构造器

java.lang.Thread 的构造函数如下:

Thread() - Allocates a new Thread object.
Thread(Runnable target) - Allocates a new Thread object.
Thread(Runnable target, String name) - Allocates a new Thread object.
Thread(String name) - Allocates a new Thread object.
Thread(ThreadGroup group, Runnable target) - Allocates a new Thread object.
Thread(ThreadGroup group, Runnable target, String name) - Allocates a new Thread object so that it has target as its run object, has the specified name as its name, and belongs to the thread group referred to by group.
Thread(ThreadGroup group, Runnable target, String name, long stackSize) - Allocates a new Thread object so that it has target as its run object, has the specified name as its name, and belongs to the thread group referred to by group, and has the specified stack size.
Thread(ThreadGroup group, String name) - Allocates a new Thread object.

共有 8 个构造函数,本次学习暂不涉及 线程组 的概念,所以在使用过程中仅用到前 4 种构造方法

run()

参考:run()

函数如下:

 /**
 * If this thread was constructed using a separate
 * <code>Runnable</code> run object, then that
 * <code>Runnable</code> object's <code>run</code> method is called;
 * otherwise, this method does nothing and returns.
 * <p>
 * Subclasses of <code>Thread</code> should override this method.
 *
 * @see     #start()
 * @see     #stop()
 * @see     #Thread(ThreadGroup, Runnable, String)
 */
@Override
public void run() {
    if (target != null) {
        target.run();
    }
}

创建线程的目的就是为了独立执行一段程序。启动线程后,Java 将自动调用 run() 方法,run() 方法执行完成或意外终止,表示该线程已结束,所以将执行程序封装在 run() 方法中

start()

参考:start()

函数如下:

/**
 * Causes this thread to begin execution; the Java Virtual Machine
 * calls the <code>run</code> method of this thread.
 * <p>
 * The result is that two threads are running concurrently: the
 * current thread (which returns from the call to the
 * <code>start</code> method) and the other thread (which executes its
 * <code>run</code> method).
 * <p>
 * It is never legal to start a thread more than once.
 * In particular, a thread may not be restarted once it has completed
 * execution.
 *
 * @exception  IllegalThreadStateException  if the thread was already
 *               started.
 * @see        #run()
 * @see        #stop()
 */
public synchronized void start() {
    /**
     * This method is not invoked for the main method thread or "system"
     * group threads created/set up by the VM. Any new functionality added
     * to this method in the future may have to also be added to the VM.
     *
     * A zero status value corresponds to state "NEW".
     */
    if (threadStatus != 0)
        throw new IllegalThreadStateException();

    /* Notify the group that this thread is about to be started
     * so that it can be added to the group's list of threads
     * and the group's unstarted count can be decremented. */
    group.add(this);

    boolean started = false;
    try {
        start0();
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
            /* do nothing. If start0 threw a Throwable then
              it will be passed up the call stack */
        }
    }
}

创建线程类实例后,调用 start() 方法,Java 虚拟机就会调用该线程的 run() 方法(不一定是实时的,取决于 Java 虚拟机的线程调度器的调度

Note 1:必须调用线程的 start() 方法才能启动线程,所以直接调用 run() 方法只是顺序执行方法里的程序而已

Note 2:单个线程仅能调用一次 start() 方法,即单个线程实例仅能运行一次;如果调用多次 start() 方法,将会抛出 IllegalThreadStateException 异常


继承 Thread

继承 Thread 类及其子类,并实现 run() 方法。示例如下:

/**
 * Created by zj on 2017/10/13.
 */
public class MyThread extends Thread {

    @Override
    public void run() {
        super.run();
    }
}

创建类对象,可使用构造函数如下:

Thread() - Allocates a new Thread object.
Thread(String name) - Allocates a new Thread object.

调用 start() 方法即可启动线程


实现接口 Runnable

继承接口 Runnable,并实现接口方法 run()。示例如下:

/**
 * Created by zj on 2017/10/16.
 */
public class MyRunnable implements Runnable {

    @Override
    public void run() {

    }
}

创建 MyRunnable 对象,同时需要新建一个 Thread 类对象,将 MyRunnable 对象作为参数输入到 Thread 类对象中,可使用构造函数如下:

Thread(Runnable target) - Allocates a new Thread object.
Thread(Runnable target, String name) - Allocates a new Thread object.

示例如下:

MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();

得到新线程后,调用 start() 方法即可启动线程


实现接口 CallableFuture

JDK 1.5 开始,Java 提供了新的接口 Callable

@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

该接口仅有一个 call() 方法,其功能和 run() 类似,同样是执行线程的程序,不过它增加了以下功能:

  • 可以有返回值
  • 可以抛出异常

由于 call() 方法是 Java 在线程启动后自动调用的,无法正常返回,所以 Java 提供了一个接口 Future,用于得到 call() 方法的返回值;同时,Java 内置了一个 Future 接口的实现类 FutureTask,该接口需要 Callable 接口作为参数,同时也实现了 Runnable 接口,所以可以作为 Thread 类构造函数的参数

使用流程如下:

  1. 创建一个 Callable 接口实现类

    class MyCallable implements Callable<String> {
    
        @Override
        public String call() throws Exception {
    
            System.out.println("Hello Callable");
    
            return "Hello World";
        }
    
    }
    
  2. 创建 Callable 实例,作为参数创建 FutureTask 实例

    MyCallable callable = new MyCallable();
    FutureTask<String> futureTask = new FutureTask<String>(callable);
    
  3. 创建线程,使用 FutureTask 实例作为参数,启动 start() 方法

    new Thread(futureTask).start();
    
  4. 调用 FutureTaskget() 方法,即可获得返回值

    try {
        String str = futureTask.get();
        System.out.println(str);
    } catch (InterruptedException | ExecutionException e) {
        e.printStackTrace();
    }
    

完整代码如下:

import java.util.concurrent.*;

/**
 * Created by zj on 2017/10/13.
 */
public class Main {

    public static void main(String[] args) {
        MyCallable callable = new MyCallable();
        FutureTask<String> futureTask = new FutureTask<String>(callable);
        new Thread(futureTask).start();
        try {
            String str = futureTask.get();
            System.out.println(str);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }

}

class MyCallable implements Callable<String> {

    @Override
    public String call() throws Exception {

        System.out.println("Hello Callable");

        return "Hello World";
    }

}

Note:调用 get() 方法后,当前线程会被阻塞,直到得到返回值为止


线程创建方式的比较

第1 / 2种方法的比较

  • 当使用继承 Thread 类的方法创建新线程时,由于单个线程仅能执行一次,所以每个线程的资源都是独立的;
  • 而当使用实现 Runnable 接口的方法时,可以创建单个 Runnable 类对象,多个线程同时使用该对象作为参数,此时,这些线程共享该对象的资源

测试函数如下:

import java.util.concurrent.*;

/**
 * Created by zj on 2017/10/13.
 */
public class Main {

    public static void main(String[] args) {
        MyRun myRun = new MyRun();
        new Thread(myRun, "MyRun-1").start();
        new Thread(myRun, "MyRun-2").start();
        new Thread(myRun, "MyRun-3").start();

        new MyThr("MyThr-1").start();
        new MyThr("MyThr-2").start();
        new MyThr("MyThr-3").start();
    }

}

class MyThr extends Thread {
    private int i;

    public MyThr(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (; i < 100; i++) {
            System.out.println("current Thread name = " + this.getName() + " i = " + i);
        }
    }
}

class MyRun implements Runnable {
    private int i;

    @Override
    public void run() {
        for (; i < 100; i++) {
            System.out.println("current Thread name = " + Thread.currentThread().getName() + " i = " + i);
        }
    }
}

从资源共享的角度来看,推荐使用 Runnable 接口方式,缺点就是操作会比较复杂(涉及线程同步,通信问题)

RunnableCallable 接口比较

Callable 接口是 Runnable 接口的增强版,如果需要返回值,可以选择使用 Callable 接口

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值