Java实现多线程有几种方式(满分回答)

  • Post author:
  • Post category:java




JDK8 创建的线程的两种方式



orcle文档解释


orcle文档



https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html


在这里插入图片描述


总结:

准确的讲,创建线程只有一种方式那就是构造Thread类,而实现线程的执行单元有两种方式:

  • 方法一:实现

    Runnable接口

    的重写run方法,并把

    Runnable

    实例传给Thread类
  • 方法二∶重写

    Thread



    run

    方法(继承Thread类)

(其他一些方式,究其根本都是间接的通过上面两种方式创建的)



方式一:继承Thread类

public static void main(String[] args) {
        System.out.println("主线程运行:" + Thread.currentThread().getName());
        Task1 task = new Task1();
        task.start();
    }

    static class Task1 extends Thread{
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                System.out.println("task1线程运行" + Thread.currentThread().getName());
                System.out.println("Task11继承Thread实现多线程");
            }
        }
    }

}
  • 自定义线程类继承Thread类

  • 重写run()方法,编写线程执行体

  • 创建线程对象,调用start()方法启动线程



方式二:实现Runnable接口

public static void main(String[] args) {
        Task task = new Task();
        Thread thread  = new Thread(task);
        thread.start();
    }

    static class Task implements Runnable{

        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                System.out.println("task1线程运行" + Thread.currentThread().getName());
                System.out.println("Task11实现Runnable接口实现多线程");
            }
        }
    }
  • 定义MyRunnable类实现Runnable接口

  • 实现run()方法,编写线程执行体

  • 创建线程对象,调用start()方法启动线程



同时用两种的情况

public static void main(String[] args) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("实现Runnable的run方法");
        }
    }){
        @Override
        public void run() {
            System.out.println("匿名内部类,重写Thread类的run方法");
        }
    }.run();
}


结果:

匿名内部类,重写Thread类的run方法


分析:

这里使用的匿名内部类,在匿名内部类里面又重写了Thread类的run方法。创建Thread类的同时传入了Runnable对象,这时候Thread类的run已被重写,不再是原生方法的逻辑:执行传入task的run

image-20230211121531177

所以即便传入Runnable,也不会被执行。



其他间接创建方式



Callable接口

public static void main(String[] args) {
    Task task = new Task();
    FutureTask<Integer> ft = new FutureTask<>(task);
    Thread thread = new Thread(ft);
    thread.start();

}

static class Task implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        for (int i = 0; i < 20; i++) {
            System.out.println("task1线程运行" + Thread.currentThread().getName());
            System.out.println("Task11实现Callable接口实现多线程");
        }
        return 1;
    }
}


FutureTask

实现了

RunnableFuture

接口,

RunnableFuture

继承了

Runnable



Future

接口。所以也是间接通过

Runnable

来创建的。

  • 实现Callable接口,需要返回值类型
  • 重写call方法,需要抛出异常
  • 创建目标对象
  • 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);
  • 提交执行:Future result1 = ser.submit(t1);
  • 获取结果: boolean r1 = result1.get()
  • 关闭服务: ser.shutdownNow();


实现Runnable接口和Callable接口的区别

  • Runnable 接口不会返回结果,Callable 接口可以返回结果
  • Callable接口实现类中的run方法允许异常向上抛出,可以在内部处理,try catch,但是runnable接口实现类中run方法的异常必须在内部处理,不能抛出



线程池


ThreadPoolExecutor

类的

execute

方法:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AEKGtqyC-1677038180765)(Java%E5%A4%9A%E7%BA%BF%E7%A8%8B%E6%B1%87%E6%80%BB%E6%80%BB%E7%BB%93%E7%AC%94%E8%AE%B0.assets/image-20230222115102680.png)]

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    /*
     * Proceed in 3 steps:
     *
     * 1. If fewer than corePoolSize threads are running, try to
     * start a new thread with the given command as its first
     * task.  The call to addWorker atomically checks runState and
     * workerCount, and so prevents false alarms that would add
     * threads when it shouldn't, by returning false.
     *
     * 2. If a task can be successfully queued, then we still need
     * to double-check whether we should have added a thread
     * (because existing ones died since last checking) or that
     * the pool shut down since entry into this method. So we
     * recheck state and if necessary roll back the enqueuing if
     * stopped, or start a new thread if there are none.
     *
     * 3. If we cannot queue task, then we try to add a new
     * thread.  If it fails, we know we are shut down or saturated
     * and so reject the task.
     */
    int c = ctl.get();
    //如果工作线程数小于核心线程数,
    if (workerCountOf(c) < corePoolSize) {
        //执行addWorker,会创建一个核心线程,如果创建失败,重新获取ctl
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    //如果工作线程数大于等于核心线程数,线程池的状态是RUNNING,并且可以添加进队列
    //(RUNNING状态下)如果添加失败,说明是队列已经满了,接着就去创建新的线程,如果大于最大线程数,则执行拒绝策略
    //如果线程池不是RUNNING状态,则执行拒绝策略(当然还会调addWorker进行判断一次)
    if (isRunning(c) && workQueue.offer(command)) {
        //再次获取ctl,进行双重检索(也就是对线程池的状态再次检查一遍)
        int recheck = ctl.get();
        //如果线程池是不是处于RUNNING的状态,那么就会将任务从队列中移除,
        //如果移除失败,则会判断工作线程是否为0 ,如果过为0 就创建一个非核心线程
        //如果移除成功,就执行拒绝策略,因为线程池已经不可用了;
        if (! isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    else if (!addWorker(command, false))
        //线程池挂了或者大于最大线程数
        reject(command);
}


addWork

方法会创建线程池内工作线程

Worker



Worker

这个线程,实现了

Runnable

接口,并持有一个线程thread,一个初始化的任务firstTask。thread是调用构造方法时通过

ThreadFactory

来创建的线程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TG3ITFgx-1677038180766)(Java%E5%A4%9A%E7%BA%BF%E7%A8%8B%E6%B1%87%E6%80%BB%E6%80%BB%E7%BB%93%E7%AC%94%E8%AE%B0.assets/image-20230222113722589.png)]


ThreadFactory

中也是

Runnable

创建线程的模式。

在这里插入图片描述



版权声明:本文为qq_37132495原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。