Java多线程学习笔记——建立新线程的4种方法

Java语言内置了多线程支持。当咱们启动一个Java程序时,其实是启动了一个JVM进程,而后JVM启动主线程来执行main()方法。在main()方法中,咱们又能够建立、启动其余线程。java

本质上来说,在Java中建立新线程十分简单,只须要实例化一个Thread实例便可:segmentfault

Thread t = new Thread();
复制代码

可是当咱们调用该实例的start()方法来启动线程时,该线程其实什么事情也没干就结束了,缘由是Thread类中覆写Runnable接口的run()方法的方法体以下:多线程

@Override
public void run() {
	if (target != null) {
		target.run();
	}
}
复制代码

这里的target字段是Runnable接口的一个实例,而当Thread构造器没传入任何参数时,target为null,线程天然也就不会执行任何操做。ide

在Java中,要建立能执行指定代码的新线程,有如下几种方法:学习

  1. Thread类派生一个自定义类,而后覆写run()方法。
  2. 建立Thread实例时,传入一个Runnadble接口的实例,一样要实现接口定义的run()抽象方法。
  3. 实现Callable接口,重写call()方法,并包装成FutureTask类对象,再做为参数传入Thread的构造器。
  4. 使用线程池建立新线程。

注意:网站

  1. 方法1和方法2由于都是经过覆写run()方法来建立新线程,而run()方法并没有返回值,因此这两种方法也没法获取返回值。
  2. 在实际项目工程中,最好不要在应用中自行显式建立线程(方法一、二、3),线程资源要经过线程池提供。使用线程池的好处是减小在建立和销毁线程上所消耗的时间以及系统资源的开销,解决资源不足的问题。若是不使用线程池,有可能形成系统建立大量同类线程而致使消耗完内存或者“过分切换”的问题。

1. 继承Thread类,覆写run()方法

public class ExecuteThread {
  public static void main(String[] args) {
    Thread t = new MyThread();
    t.start();	// 启动新线程
  }
}

// 继承自Thread的自定义类
class MyThread extends Thread {
  @Override
  public void run() {
    System.out.println("start new thread!");
  }
}
复制代码

若是该类型的线程只须要使用一次,也能够用匿名内部类的方式让代码更加简单:编码

public class ExecuteThread {
  public static void main(String[] args) {
    Thread t = new Thread() {
      // 内部匿名类对run()方法的覆写
    	@Override
  		public void run() {
    		System.out.println("start new thread!");
  		}
    };
    t.start();	// 启动新线程
  }
}
复制代码
  • 优势:编码简单,容易理解。
  • 缺点:不能继承其余类,功能单一。

2. 实现Runnadble接口,实现run()抽象方法

public class ExecuteThread {
    public static void main(String[] args) {
        Thread t = new Thread(new MyRunnable());
        t.start(); // 启动新线程
    }
}

class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("start new thread!");
    }
}
复制代码

或者用Java8引入的Lambda语法进一步简写为:spa

public class ExecuteThread {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            System.out.println("start new thread!");
        });
        t.start(); // 启动新线程
    }
}
复制代码
  • 优势:
    1. 传入的实例实现的是Runnadble接口,因此能够继承其余的类,避免了单继承的局限性。
    2. 适合多个相同程序代码的线程共享一个资源(同一个线程任务对象根据传入构造器的不一样Runnadble接口实例,能够被包装成多个线程对象),实现解耦操做,代码和线程独立。
  • 缺点:实现相对复杂一些。

3. 实现Callable接口,重写call()方法,并包装成FutureTask对象传入Thread构造器

public class ExecuteThread {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<String> callableInstance = new MyCallable<>("return value");
        // 使用FutureTask类包装Callable接口的实例,该对象封装了Callable接口实现实例的call()方法的返回值。
        FutureTask<String> task = new FutureTask<>(callableInstance);
        Thread t = new Thread(task);
        t.start();  // 启动新线程
        // 调用FutureTask实例的get()方法获取新线程执行结束返回值。
        System.out.println(task.get());
    }
}

class MyCallable<V> implements Callable<V> {
    private V toReturn;

    public MyCallable (V val) {
        toReturn = val;
    }

    @Override
    public V call() throws Exception{
        System.out.println("start new thread!");
        return toReturn;
    }
}
复制代码
  • 优势:相对于方法2(传入Runnadble实例),能够获取返回值。
  • 缺点:实现比较复杂。

4. 使用线程池建立新线程

线程池的使用避免了由于频繁建立、销毁线程带来的大量系统开销,实现了资源的复用。具体的使用方法看这篇笔记:Java多线程学习笔记——如何使用线程池线程

参考连接

相关文章
相关标签/搜索