Java创建多线程的四种方式

版权声明:转载请标明附带连接标明出处 https://blog.csdn.net/Hollake/article/details/89471058

java有以下四种创建多线程的方式:

  • 1):继承Thread类创建线程
  • 2)实现Runnable接口创建线程
  • 3)使用Callable和FutureTask创建线程
  • 4)使用线程池,例如用Executor框架创建线程

1)继承Thread类创建线程步骤如下:

  1. 定义一个继承Thread线程类的类,在类中重写run方法。
  2. 在main函数中实例化这个类,初始化实现r继承Thread类的对象。
  3. 用这个类的实例调用start方法,执行覆写的run方法。这里是父类即Thread接收创建的对象,由于自身没有start方法,所以是调用父类Thread的start方法创建线程。

2)实现Runnable接口创建线程步骤如下:

  1. 定义一个实现Runnable接口的类A,在类中重写run方法。
  2. 在main函数中实例化这个类,但是注意,这不是线程类或者其子类,无法使用start方法,所以无法像1)方法一样用父类接收其对象直接调用start方法执行run方法。
  3. 实例化线程类,将上一步实例化的Runnable接口的类作为参数传给Thread线程类,初始化线程对象,即 Thread t = new Thread(new A());
  4. 线程类实例调用start方法执行run方法体。

为什么不直接调用run方法,而是必须先调用start方法呢?

因为start方法会启动线程,此时该线程处于就绪状态,start方法体中有一个start0的native方法,在这个方法里创建了线程,并且执行调用了重写的run方法,在run方法结束时,线程便会中止,也就是start0方法退出时。如果是直接调用run方法,即调用重写的run方法,这和普通方法没有什么区别,不会创建新线程,因为创建新线程,使线程处于就绪状态的操作在start方法中,而run方法会被jvm在start方法中的start0中调用。

start0方法源码解析请看https://www.jianshu.com/p/81a56497e073

这里再多思考一下:

一个线程可以多次调用start方法吗

不能,会抛出下列异常,也就是非法线程状态异常,属于运行异常。查看源码可以看到在执行start时,会对线程状态进行判断,如果线程已经启动过,那么通常情况下无法再次进行启动,这一点在源码注释中也可以看到。即在同一个线程只能start一次,多次调用start方法会抛出异常。当调用start方法时,线程会被添加到线程组中,等待线程调度器调用,当获取到资源时,就进入运行状态。

源码分析请看https://cloud.tencent.com/developer/article/1386458

Exception in thread "main" java.lang.IllegalThreadStateException
	at java.lang.Thread.start(Thread.java:708)
	at thread.MyThread.main(MyThread.java:13)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

 好了我们言归正传,继续看第三种创建线程的方法

3)使用Callable和FutureTask创建线程步骤如下:

  1. 定义一个Callable接口的实现类。
  2. 创建Callable实现类对象传递给FutureTask构造器。
  3. 将FutureTask对象传递给Thread构造器,Thread对象调用start方法启动线程,这一步新线程已经创建完成,处于就绪状态
  4. 通过FutureTask对象的get方法获取线程运行的结果。

4)使用线程池,例如用Executor框架步骤如下:

  1. 使用Executors工具类中的静态工厂方法用于创建线程池。
  2. 创建线程池使用execute方法启动线程。
  3. 使用shutdown方法等待提交的任务执行完成并后关闭线程。

Runnable和Callable有什么区别?

  1. Runnable接口定义的run方法,Callable定义的是call方法。
  2. run方法没有返回值,call方法必须有返回值。
  3. run方法无法抛出异常,call方法可以抛出checked exception。
  4. Callable和Runnable都可以应用于executors。而Thread类只支持Runnable.

几种方法优缺点对比:https://blog.csdn.net/sinat_27933301/article/details/69944286 

代码示例: 

package thread;

import java.util.concurrent.*;

/**
 * Created by Administrator on 2019\4\23 0023.
 */
public class MyThread {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
//      创建线程的第一种方法
        Thread1 thread1 = new Thread1();
        thread1.start();

//      创建线程的第二种方法
        Thread2 thread2 = new Thread2();
        Thread thread = new Thread(thread2);
        thread.start();

//      创建线程的第三种方法
        Callable<String> callable = new Thread3();
        FutureTask<String> futureTask = new FutureTask<>(callable);
        Thread thread3 = new Thread(futureTask);
        thread3.start();
        String s = futureTask.get();
        System.out.println(s);

//      创建线程的第四种方法
        Executor executor = Executors.newFixedThreadPool(5);
        executor.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread()+"创建线程的第四种方法");
            }
        });
        ((ExecutorService) executor).shutdown();

    }
}
class Thread1 extends Thread{
    @Override
    public void run() {
        System.out.println(Thread.currentThread()+"创建线程的第一种方法");
    }
}

class Thread2 implements Runnable {

    @Override
    public void run() {
        System.out.println(Thread.currentThread()+"创建线程的第二种方法");
    }
}

class Thread3 implements Callable<String> {

    @Override
    public String call() throws Exception {
        return Thread.currentThread()+"创建线程的第三种方法";
    }
}

运行结果:

可以看出创建了四个线程,而且最后一个线程是线程池所创建。

Thread[Thread-0,5,main]创建线程的第一种方法
Thread[Thread-1,5,main]创建线程的第二种方法
Thread[Thread-2,5,main]创建线程的第三种方法
Thread[pool-1-thread-1,5,main]创建线程的第四种方法

猜你喜欢

转载自blog.csdn.net/Hollake/article/details/89471058