精华内容
下载资源
问答
  • 2020-12-20 15:52:50

    多线程的实现方式

    1.1方式一:继承Thread类【应用】

    • 实现步骤
      • 定义一个类MyThread继承Thread类
      • 在MyThread类中重写run()方法
    public class Mythread extends Thread {
        @Override
        public void run() {
        //    代码就是线程在开启后执行的代码
        //    快捷键  100.fori  回车
            for (int i = 0; i < 100; i++) {
                System.out.println("线程开启了"+i);
            }
        }
    }
    
    • 创建MyThread类的对象
    • 启动线程
    public class Demo {
        public static void main(String[] args) {
            //创建一个线程对象
            Mythread t1 = new Mythread();
            //创建一个线程对象
            Mythread t2 = new Mythread();
            //开启一个线程
            t1.start();
            //开启第二个线程
            t2.start();
            //没有开启线程,按顺序执行
            t1.run();
            t2.run();
        }
    }
    
    • 运行结果

    在这里插入图片描述

    • 两个小问题

      • 为什么要重写run()方法?

        因为run()是用来封装被线程执行的代码

      • run()方法和start()方法的区别?

        run():封装线程执行的代码,直接调用,相当于普通方法的调用,没有开启线程

        start():启动线程;然后由JVM调用此线程的run()方法

    1.2方式二:实现Runnable接口【应用】

    • 实现步骤

      • 定义一个类MyRunnable实现Runnable接口
      • 在MyRunnable类中重写run()方法
    public class MyRunnable implements Runnable {
        @Override
        public void run() {
            //线程启动后执行的代码
            for (int i = 0; i < 100; i++) {
                System.out.println("线程开启"+i);
            }
        }
    }
    
    • 创建MyRunnable类的对象
    • 创建Thread类的对象,把MyRunnable对象作为构造方法的参数
    • 启动线程
    public class Demo {
        public static void main(String[] args) {
            //创建一个参数对象
            MyRunnable myRunnable1 = new MyRunnable();
            //myRunnable.start  没有start方法
            //创建一个线程Thread对象,把myRunnable作为参数传递进去
            Thread t1 = new Thread(myRunnable1);
            //线程启动后就执行的是参数里面的run方法
            t1.start();
    
            MyRunnable myRunnable2 = new MyRunnable();
            Thread t2 = new Thread(myRunnable2);
            t2.start();
    
        }
    }
    
    • 运行结果

    在这里插入图片描述

    1.3方式三: 实现Callable接口【应用】

    • 实现步骤

      • 定义一个类MyCallable实现Callable接口
      • 在MyCallable类中重写call()方法
    public class MyCallable implements Callable<String> {
        @Override
        public String call() throws Exception {
            //返回值表示线程运行之后返回的结果Callable<Object> 泛型就是返回值得类型
            for (int i = 0; i < 100; i++) {
    
                System.out.println("跟女孩表白"+i);
            }
            return "答应";
        }
    }
    
    • 创建MyCallable类的对象

    • 创建Future的实现类FutureTask对象,把MyCallable对象作为构造方法的参数

    • 创建Thread类的对象,把FutureTask对象作为构造方法的参数

    • 启动线程

    • 再调用get方法,就可以获取线程结束之后的结果。

    • 代码演示

    public class Demo {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            //创建MyCallable对象
            MyCallable myCallable1 = new MyCallable();
            //可以获取线程执行完毕的结果,也可以作为参数传递给Thread对象
            FutureTask<String> futureTask = new FutureTask<>(myCallable1);
            //创建对象开启线程
            Thread t1 = new Thread(futureTask);
            //String s = futureTask.get();
            t1.start();
            //获取线程的返回结果
            // get()方法需要在线程开启后执行,如果线程运行还没有结束,get方法会在这里死等
            String s = futureTask.get();
            System.out.println(s);
    
        }
    }
    
    • 运行结果

    在这里插入图片描述

    1.4 三种实现方式的对比

    • 实现Runnable、Callable接口
      • 好处: 扩展性强,实现该接口的同时还可以继承其他的类
      • 缺点: 编程相对复杂,不能直接使用Thread类中的方法
      • Callable接口可以获取线程的返回值
    • 继承Thread类
      • 好处: 编程比较简单,可以直接使用Thread类中的方法
      • 缺点: 可以扩展性较差,不能再继承其他的类
    更多相关内容
  • Java多线程三种实现方式区别

    千次阅读 2020-03-03 14:03:21
    一、什么情况下创建线程不使用线程池? 1、需要定义线程的优先级; 2、需要创建前台线程; 3、需要手动终止线程; 4、线程执行时间长(线程池适用于多而执行时间端的线程) ...三种实现方式区别 ...

    一、什么情况下创建线程不使用线程池?

    1、需要定义线程的优先级;

    2、需要创建前台线程;

    3、需要手动终止线程;

    4、线程执行时间长(线程池适用于多而执行时间端的线程)

    二、创建线程的三种方式,以及它们的优缺点。

    1、继承Thread类;
    在这里插入图片描述
    2、实现Runable接口;
    在这里插入图片描述
    3、实现Callable接口。
    在这里插入图片描述
    三、三种实现方式区别
    在这里插入图片描述

    展开全文
  • Java多线程实现的三种方式

    千次阅读 2021-02-12 10:01:26
    一、Java 多线程实现方式java中实现多线程方式三种,接下来我将会逐个进行介绍。1.继承Thread类继承Thread类是Java中比较常见,也是很基础的一实现Java多线程方式。实现的方式也比较简单,只要将需要实现...

    对于所有语言来说,多线程的编程是绝不可少的。同样的Java语言也包含了多线程的开发。首先,我们先来了解一下Java语言的多线程实现方式。

    一、Java 多线程实现方式

    java中实现多线程的方式有三种,接下来我将会逐个进行介绍。

    1.继承Thread类

    继承Thread类是Java中比较常见,也是很基础的一种实现Java多线程的方式。实现的方式也比较简单,只要将需要实现多线程的Java类继承java.lang.Thread类即可。

    class MyThread extends Thread{

    private String name;

    public MyThread(String name){

    this.name = name;

    }

    @Override

    public void run() {

    Thread.currentThread().setName(name);

    System.out.println("I am Thread :" +name);

    }

    }

    如上代码片段则是通过继承Thread类实现多线程的方式之一。那么多线程该如何调用呢?请接着阅读下边的代码。

    public class threadLearn {

    public static void main(String[] args)  {

    //实例化继承了Thread的类

    MyThread thread1 = new MyThread("Thread1");

    //通过从Thread类中所继承的start()方法启动线程;

    thread1.start();

    }

    }

    运行上边的代码,可以得到结果:

    8947670.html

    1876c264d8f0efa59ab4af22750085be.png

    到这里,一个简单的线程已经被启动了。下边我们接着介绍另外两种多线程的实现方式。

    2.实现Runable接口

    接下来请看这样一种场景,DogRobot是一个用来看门的Dog,现在需要多个Dog分别把守不同的位置,但DogRobot一定要继承Robot父类,此时应该如何实现多线程呢?

    在这种场景下,可以通过实现Runable接口的方式来实现一个类的多线程。

    我们知道,在Java中,类的继承是单一的,即一个子类仅可以继承一个父类(一个儿子只有一个爹,符合自然规律),但可以实现多个接口。那么,通过Runable接口来实现多线程的好处自然不言而喻。

    接下来看一下实现方式:

    public class threadLearn {

    public static void main(String[] args)  {

    //实例化继承了Thread的类

    MyThread thread1 = new MyThread("Thread1");

    //通过从Thread类中所继承的start()方法启动线程;

    thread1.start();

    /*

    * 实现Runnable的类,需要将其放在一个Thread实例对象中,

    * 由Thread实例对象进行线程的启动及控制。

    */

    Thread threadDog = new Thread(new DogRobot("kiki"));

    threadDog.start();

    }

    }

    class DogRobot extends Robot implements Runnable{

    private String name;

    public DogRobot(String name){

    super(name);

    this.name = name;

    }

    @Override

    public void run() {

    Thread.currentThread().setName(name);

    System.out.println("I am DogRobot :" +name);

    }

    }

    class Robot {

    private String Name;

    public Robot(String name)

    {

    this.Name = name;

    }

    }

    8947670.html

    9fab246c654fc1c3a797e8710715730f.png

    下边接着讲解第三种多线程的实现方式。

    3. 使用Executor框架实现多线程

    在介绍Excutor实现多线程之前,我们先来学习另外一种多线程的实现方式,即继承Callable接口实现多线程的方式。

    首先我们来看一下Callable的接口:

    83a33d1f5de2128db2c48b0eb52efab4.png

    8947670.html

    可以看到,Callable的接口只有一个方法,那么我们在实现这个接口的时候也仅需要实现这个方法即可。

    /*

    * Callable接口实现多线程Demo

    */

    classMyCallable implements Callable

    {

    @Override

    public V call() throws Exception {

    // TODO Auto-generated method stub

    System.out.println("I am Callable thread : "+Thread.currentThread().getName());

    return null;

    }

    }

    如何调用这个线程,使其执行任务呢?那么,是需要通过FutureTask这个类的实例去调度,我们首先来看一下FutureTask类的结构:

    public class FutureTask implements RunnableFuture

    这是FutureTask的实现方式,我们可以看到FutrueTask是实现了RunnableFuture的方法,那么RunnableFuture又是做什么的呢?我们接着跟进去看看结构:

    public interface RunnableFuture extends Runnable, Future {

    /**

    * Sets this Future to the result of its computation

    * unless it has been cancelled.

    */

    void run();

    }

    以上您所看到的,正是RunnableFuture接口的结构,我们可以看到,这个接口,是继承了Runnable接口,和Future接口的一个接口,至于Future接口是什么,我们后续会讲到。

    如果您看到了Runnable接口,那么我想应该已经明了了,实现了Runnable接口的类,可以通过Thread实例对象来驱动,从而运行一个独立的线程。这是我们前边所讲到的。

    到这里,还有一个问题,FutureTask和Callable接口有什么关系呢?

    那么我们接着看FutureTask的构造方法:

    /**

    * Creates a {@code FutureTask} that will, upon running, execute the

    * given {@code Callable}.

    *

    * @paramcallable the callable task

    * @throws NullPointerException if the callable is null

    */

    public FutureTask(Callable callable) {

    if (callable == null)

    throw new NullPointerException();

    this.callable = callable;

    this.state = NEW; // ensure visibility of callable

    }

    可以看到,FutureTask的构造方法之一所需要的参数,就是Callable的实例对象。为什么说之一呢,因为还有一个构造方法,是通过Runnable接口实现的,如何实现的呢:

    public FutureTask(Runnable runnable, V result) {

    this.callable = Executors.callable(runnable, result);

    this.state = NEW; // ensure visibility of callable

    }

    其实我们继续跟入进去可以发现,其实这个方法本质上还是生成了一个Callable的对象,最后赋予自己内部的this.callable;感兴趣的同学可以自己试一试这种方式。

    回到我们的话题,继续实现调用:

    public static void main(String[] args)  {

    /*

    * 使用Callable来创建线程

    */

    Callable aCallable = new MyCallable();

    FutureTask aTask = new FutureTask(aCallable);

    Thread aThread = new Thread(aTask);

    aThread.start();

    }

    我们运行一下程序,可以看到,运行的结果如下图所示:

    bb658edfb5842d20887eb71e0ce3c751.png

    8947670.html

    到这里,一个通过Callable实现的接口便是成功了。

    那么这里会有一个问题,按照这样的实现方式,那和Runnable接口有什么区别???

    其实我们应该注意到了,Callable接口里的call方法,是一个有返回值的方法;

    且FutureTask类的实现方式中,针对Runnable的实现方式,也是携带有一个参数result,由result和Runnable实例去合并成一个Callable的实例。

    小本本拿出来划重点了。

    实现了Callable接口的线程,是具有返回值的。而对于一些对线程要求有返回值的场景,是非常适用的。

    接下来,我们就看一下,如何获通过Callable接口获取线程的返回值。

    获取Callable接口的返回值,需要使用Future接口的实例化对象通过get的方式获取出来。

    Mark -- 2018 04 24

    今天接着学习多线程。昨天说到通过实现Callable接口实现多线程,今天我们来看如何通过Callable线程获取到线程的返回值。

    首先我们先来认识几个接口及一个工厂类

    ExecutorService 接口

    public interface ExecutorService extends Executor

    ExecutorService接口是实现线程池经常使用的接口。通常我们使用的线程池、定时任务线程池都是他的实现类。

    Executors工厂类

    public class Executors

    Executors是一个工厂类,通过调用工厂类的方法,可以返回各种线程池的实现类。比如我们接下来要用到的:

    public static ExecutorService newFixedThreadPool(int nThreads) {

    return new ThreadPoolExecutor(nThreads, nThreads,

    0L, TimeUnit.MILLISECONDS,

    new LinkedBlockingQueue());

    }

    我们可以看到,返回值的类型是一个ExecutorService 而实际上返回的返回值,则是一个ThreadPoolExecutor ,我们接着跟进去看下ThreadPoolExecutor是什么:

    public class ThreadPoolExecutor extends AbstractExecutorService

    到这里,我们可以看到,ThreadPoolExecutor是一个继承了AbstractExecutorService 的实现类。

    这个线程池类里定义了各种我们需要对线程池进行操作的方法。后续有时间我们再研究一下源码。

    有了上述的这些基本了解,我们则可以进行线程池的创建:

    int taskSize = 5;

    ExecutorService exPool = Executors.newFixedThreadPool(taskSize);

    如上边这样的代码,我们就可以创建一个具有5个线程容量的线程池。

    那么如何将Callabe接口添加到线程池中运行呢?

    ExcutorService提供了一个方法供我们调用:

    Future submit(Callable task);

    我们看到,这个submit方法也只是接口里定义的一个方法,那么他的实现方法是什么呢,我们代码里跟一下:

    b62e854f6f8c26a2cc24248b8e3dfaac.png

    8947670.html

    可以看到,有很多个实现的方法,那么,我们这里调用的是哪一个呢?

    很显然,我们调用的是AbstractExecutorService中的实现方法。因为用来调用submit的实例对象实际上正是ThreadPoolExecutor,刚才我们也提到了,ThreadPoolExecutor是继承了AbstractExcutorService的。

    那么结果应该相当的明显了吧。

    /**

    * @throws RejectedExecutionException {@inheritDoc}

    * @throws NullPointerException {@inheritDoc}

    */

    public Future submit(Callable task) {

    if (task == null) throw new NullPointerException();

    RunnableFuture ftask = newTaskFor(task);

    execute(ftask);

    return ftask;

    }

    看到这里是不是觉得有一丝丝的熟悉,正如我们第三小节所提到的 RunnableFuture ,从本质上,我们还是返回了一个RunnableFuture类型的实例。因此,我们可以通过该实例获取到线程的返回值。

    那么接下来,我们写一个小demo来实践一下,通过ExcutorService来加载Callable接口实现多线程,并且得到返回值。

    首先,我们来写一个Callable接口的实现类

    /*

    * Callable接口实现多线程Demo

    */

    classMyCallable implements Callable

    {

    private int task_id;

    MyCallable(int task_id)

    {

    this.task_id = task_id;

    }

    @Override

    public Object call() throws Exception {

    // TODO Auto-generated method stub

    System.out.println("I am Callable thread : "+Thread.currentThread().getName());

    Random rd = new Random();

    int leng = rd.nextInt(9)+1;

    int sum = 0 ;

    for(int i = 0 ; i <= leng ; i++ )

    {

    Thread.sleep(1000);

    sum += i;

    }

    System.out.println("I am Callable thread : "+Thread.currentThread().getName()

    +"Thread name is : ["+this.task_id+"]"

    +" I worked done at ["+new Date().getTime()+"]"

    +" Random Num = "+leng

    );

    return "The task ["+this.task_id+"] get result :【 "+ sum +" 】";

    }

    }

    这个线程会返回1-10以内的随机数的自增合。我们来通过线程池调度一下:

    /*

    *使用Excutor来执行线程,并获取返回值

    */

    int taskSize = 5;

    ExecutorService exPool = Executors.newFixedThreadPool(taskSize);

    //使用Future来获取线程的结果

    List list = new ArrayList();

    for (int i = 0; i < taskSize; i++) {

    Callable c = new MyCallable(i);

    // 执行任务并获取Future对象

    Future f = exPool.submit(c);

    list.add(f);

    }

    exPool.shutdown();

    System.out.println("The time we shutDown The Executor Pool : 【"+new Date().getTime()+"】");

    for (Future f : list) {

    // 从Future对象上获取任务的返回值,并输出到控制台

    try {

    System.out.println(">>>" + f.get().toString());

    } catch (InterruptedException e) {

    // TODO Auto-generated catch block

    e.printStackTrace();

    } catch (ExecutionException e) {

    // TODO Auto-generated catch block

    e.printStackTrace();

    }

    }

    我们创建了一个List用来存放每个线程的执行结果。

    shutdown()方法并不是终止线程池,而是在执行shutdown()方法之后,线程池则不会再接收新加入的线程。

    我们运行一下 ,下边的是运行的结果:

    8947670.html

    d9b465cb3fe1f804322625946a1869f3.png

    从这个结果上,我们可以看到,线程4抽到的随机数是1,那么他执行的时间是最短的,只sleep 1S, 其他的线程2/3/1分别sleep了3/4/7S,线程5Sleep了9S,有意思的事情发生了。

    我们这条长长的输出实在线程执行完自己的计算之后输出的,还没有返回值,此时其他的几个线程的输出结果已经答应出来了,线程5在执行完毕之后,主线程才打印出了线程5的结果。

    到这里,关于Java线程的创建就告一段落。随后我们将会继续深入学习Java线程以及线程池的知识。

    展开全文
  • 创建多线程(8种方式

    千次阅读 2022-03-29 20:21:43
    无论有多少形式,创建多线程的真正的方法, 其实只有两: > 继承 Thread 类 > 实现 Runnable 接口 其它形式都是这两种方式的变体。 1、继承 Thread 类 ①实现方式 第一步:继承 Thread 类 第二步...

    无论有多少种形式,创建多线程的真正的方法,

    其实只有两种:

             > 继承 Thread 类

            > 实现 Runnable 接口

    其它形式都是这两种方式的变体。

    1、继承 Thread 类

    ①实现方式

    • 第一步:继承 Thread 类
    • 第二步:重写 run() 方法
    • 第三步:创建 Thread 子类对象
    • 第四步:调用 start() 方法启动线程

    ②start() 方法和 run() 方法区别

    调用 run() 方法仅仅只是调用了一个子类中重写的父类方法,并没有真正开启一个新的线程,还是在当前线程运行,也就是 main 线程。

    ③评价

    因为 Java 是单继承的,一个类继承了 Thread 类就不能继承其它类,所以通常不采用这个办法创建多线程。

    2、实现 Runnable 接口

    ①实现 Runnable 接口形式

    public class CreateThread02Impl {
    
        public static void main(String[] args) {
    
            // 第四步:创建实现了 Runnable 接口的类的对象
            MyRunnableThread runnable = new MyRunnableThread();
    
            // 第五步:创建 Thread 类对象
            // 参数1:runnable 对象
            // 参数2:线程名称
            Thread thread = new Thread(runnable, "thread 002");
    
            // 第六步:调用 Thread 对象的 start() 方法启动线程
            thread.start();
        }
    
    }
    
    // 第一步:实现 Runnable 接口
    class MyRunnableThread implements Runnable {
    
        // 第二步:实现 run() 方法
        @Override
        public void run() {
    
            // 第三步:编写线程中的逻辑代码
            System.out.println(Thread.currentThread().getName() + " is working");
        }
    }

    ②匿名内部类形式

    // 第一步:以匿名内部类的方式创建 Runnable 接口类型的对象
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            // 第二步:编写线程中的逻辑代码
            System.out.println(Thread.currentThread().getName() + " is working");
        }
    };
    
    // 第三步:创建 Thread 类对象
    // 参数1:runnable 对象
    // 参数2:线程名称
    Thread thread = new Thread(runnable, "thread 003");
    
    // 第四步:调用 Thread 对象的 start() 方法启动线程
    thread.start();

    ③Lambda 表达式形式

    // 编写 Lambda 表达式的口诀:
    // 复制小括号
    // 写死右箭头
    // 落地大括号
    
    // 第一步:以匿名内部类的方式创建 Runnable 接口类型的对象
    Runnable runnable = () -> {
        // 第二步:编写线程中的逻辑代码
        System.out.println(Thread.currentThread().getName() + " is working");
    };
    
    // 第三步:创建 Thread 类对象
    // 参数1:runnable 对象
    // 参数2:线程名称
    Thread thread = new Thread(runnable, "thread 004");
    
    // 第四步:调用 Thread 对象的 start() 方法启动线程
    thread.start();

    3、使用 Callable 接口配合 FutureTask

     在JDK1.5提供了Future和Callable的实现,可以用于阻塞式获取结果

    该方案最核心的价值是:使用 Callable 接口限定的功能 + Future 接口限定的功能 = 汇总各个线程执行结果

    最终执行汇总操作的这一步会被阻塞,直到前面各个线程完成了计算。

    ①FutureTask类和Runnable接口的关系

    从继承关系能够看到,FutureTask本身也间接实现了Runnable接口。FutureTask类的对象也是Runnable接口的实例,可以用于在创建Thread对象时,传入Thread构造器。

    ②Future 接口

    boolean cancel(boolean mayInterruptIfRunning);
    如果尚未启动,它将停止任务。如果已启动,则仅在 mayInterrupt 为 true 时才会中断任务。
    
    V get() throws InterruptedException, ExecutionException;
    如果任务完成,它将立即返回结果,否则将等待任务完成,然后返回结果。
    
    boolean isDone();
    如果任务完成,则返回true,否则返回false。
    

     ③FutureTask 类的构造器

    【1】.介绍:

    FutureTask 类兼具 Runnable 和 Future 接口的功能,并方便地将两种功能组合在一起。关于 FutureTask 类的使用有如下建议:

    • 在主线程中需要执行比较耗时的操作时,但又不想阻塞主线程时,可以把这些作业交给 Future 对象在后台完成
    • 当主线程将来需要时,就可以通过 Future 对象获得后台作业的计算结果或者执行状态
    • 一般 FutureTask 多用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。
    • 仅在计算完成时才能检索结果;如果计算尚未完成,则阻塞 get() 方法
    • 一旦计算完成,就不能再重新开始或取消计算
    • get() 方法而获取结果只有在计算完成时获取,否则会一直阻塞直到任务转入完成状态,然后会返回结果或者抛出异常
    • get() 只执行一次,因此get() 方法放到最后

    【2】可以使用的构造器

        public FutureTask(Callable<V> callable) {
            if (callable == null)
                throw new NullPointerException();
            this.callable = callable;
            this.state = NEW;       // ensure visibility of callable
        }

    根据这个构造器,我们知道,创建 FutureTask 对象时,传入一个 Callable 类型的对象即可。

    ④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() 方法的声明我们可以看出,它有一个返回值。这个返回值可以将当前线程内计算结果返回。

    ⑤测试代码

    // 1.创建三个FutureTask对象,封装三个线程的执行逻辑
    FutureTask<Integer> task01 = new FutureTask<>(() -> {
    
        int result = (int) (Math.random() * Math.random() * 100);
    
        System.out.println(Thread.currentThread().getName());
    
        return result;
    });
    FutureTask<Integer> task02 = new FutureTask<>(() -> {
    
        int result = (int) (Math.random() * Math.random() * 1000);
    
        System.out.println(Thread.currentThread().getName());
    
        return result;
    });
    FutureTask<Integer> task03 = new FutureTask<>(() -> {
    
        int result = (int) (Math.random() * Math.random() * 10000);
    
        System.out.println(Thread.currentThread().getName());
    
        return result;
    });
    
    // 2.创建三个线程对象,然后启动线程
    new Thread(task01, "thread01").start();
    new Thread(task02, "thread02").start();
    new Thread(task03, "thread03").start();
    
    // 3.上面三个线程执行完成后,可以收集它们各自运算的结果
    Integer task01Result = task01.get();
    Integer task02Result = task02.get();
    Integer task03Result = task03.get();
    
    System.out.println("task01Result = " + task01Result);
    System.out.println("task02Result = " + task02Result);
    System.out.println("task03Result = " + task03Result);

    ⑥Callable和Runnable对比

    Runnable接口

    Callable接口

    重写run()方法

    重写call()方法

    run()没有返回值

    call()有返回值

    run()没有声明抛出异常

    call()声明抛出Exception

    没有汇总各个线程结果的机制

    有汇总各个线程结果的机制

    ⑦Callable接口方案的特点

    该方案仅在具体任务计算完成时才能检索结果;如果计算尚未完成,则阻塞 get() 方法。一旦计算完成,就不能再重新开始或取消计算。get() 方法获取结果只有在计算完成时获取,否则会一直阻塞直到任务转入完成状态,然后会返回结果或者抛出异常, 且只计算一次。

    4、线程池

    1.参考代码

    // 1.创建线程池对象
    ExecutorService pool = Executors.newFixedThreadPool(5);
    
    // 2.给线程池对象分配任务,每一个任务是一个线程
    pool.execute(() -> {
        System.out.println(Thread.currentThread().getName() + " " + new Date());
    });
    
    pool.execute(() -> {
        System.out.println(Thread.currentThread().getName() + " " + new Date());
    });
    
    pool.execute(() -> {
        System.out.println(Thread.currentThread().getName() + " " + new Date());
    });

    2.开发建议:

    阿里开发手册中对线程创建的规定:

    结论:实际开发中,建议使用『自定义线程池』的方式创建多线程。在创建自定义线程池时,使用合理的参数。

    5.CompletableFuture

    这个completableFuture是JDK1.8版本新引入的类。下面是这个类。实现了俩接口。本身是个class。这个是Future的实现类。

     

    CompletableFuture提供了四个静态方法用来创建CompletableFuture对象:

    public static CompletableFuture<Void>   runAsync(Runnable runnable)
    public static CompletableFuture<Void>   runAsync(Runnable runnable, Executor executor)
    public static <U> CompletableFuture<U>  supplyAsync(Supplier<U> supplier)
    public static <U> CompletableFuture<U>  supplyAsync(Supplier<U> supplier, Executor executor)

    Asynsc表示异步,而supplyAsyncrunAsync不同在与前者异步返回一个结果,后者是void.第二个函数第二个参数表示是用我们自己创建的线程池,否则采用默认的ForkJoinPool.commonPool()作为它的线程池.其中Supplier是一个函数式接口,代表是一个生成者的意思,传入0个参数,返回一个结果.

    - runAsync方法不支持返回值。

    - supplyAsync可以支持返回值。

     计算完成时回调方法

    public CompletableFuture<T>     whenComplete(BiConsumer<? super T,? super Throwable> action)
    public CompletableFuture<T>     whenCompleteAsync(BiConsumer<? super T,? super Throwable> action)
    public CompletableFuture<T>     whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor)
    public CompletableFuture<T>     exceptionally(Function<Throwable,? extends T> fn)

    whenComplete可以处理正常或异常的计算结果

    exceptionally处理异常情况

    BiConsumer<? super T,? super Throwable>可以定义处理业务

    whenComplete 和 whenCompleteAsync 的区别:

    • whenComplete:是执行当前任务的线程执行继续执行 whenComplete 的任务。

    • whenCompleteAsync:是执行把 whenCompleteAsync 这个任务继续提交给线程池来进行执行。

    ==注意:方法不以Async结尾,意味着Action使用相同的线程执行,而Async可能会使用其他线程执行(如果是使用相同的线程池,也可能会被同一个线程选中执行)==

    线程串行化与并行化方法

    thenApply 方法:当一个线程依赖另一个线程时,获取上一个任务返回的结果,并返回当前任务的返回值

    public <U> CompletableFuture<U>     thenApply(Function<? super T,? extends U> fn)
    public <U> CompletableFuture<U>     thenApplyAsync(Function<? super T,? extends U> fn)
    public <U> CompletableFuture<U>     thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)

    thenAccept方法:消费处理结果。接收任务的处理结果,并消费处理,无返回结果。

    public CompletableFuture<Void>  thenAccept(Consumer<? super T> action)
    public CompletableFuture<Void>  thenAcceptAsync(Consumer<? super T> action)
    public CompletableFuture<Void>  thenAcceptAsync(Consumer<? super T> action,
                                                         Executor executor)

    thenRun方法:只要上面的任务执行完成,就开始执行thenRun,只是处理完任务后,执行 thenRun的后续操作

    public CompletableFuture<Void> thenRun(Runnable action)
    public CompletableFuture<Void> thenRunAsync(Runnable action)
    public CompletableFuture<Void> thenRunAsync(Runnable action,
                                                    Executor executor)

    带有Async默认是异步执行的。这里所谓的异步指的是不在当前线程内执行。

    6、并行计算[了解]

    List<String> list = Arrays.asList("a", "b", "c", "d", "e");
    
    // 串行计算
    list.stream().forEach(System.out::print);
    
    System.out.println();
    
    // 并行计算
    list.parallelStream().forEach(System.out::print);

     7、Timer 定时任务[了解]

    // 1、创建 Timer 对象封装定时任务中要执行的操作
    // 每一个 Timer 对象会使用一个线程来执行定时任务
    Timer timer01 = new Timer();
    
    // 2、调用 schedule() 指定任务和执行周期
    // 参数1:timerTask 封装具体任务操作
    // 参数2:delay 指定定时任务延迟多久后开始执行
    // 参数3:period 指定定时任务执行的时间间隔
    timer01.schedule(new TimerTask() {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() +" is working");
        }
    }, 0, 1000);
    
    Timer timer02 = new Timer();
    
    timer02.schedule(new TimerTask() {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() +" is working");
        }
    }, 0, 1000);

    8、Spring 异步方法[了解]

    在 Spring 环境下,如果组件 A(假设是 ControllerA)要调用组件 B(假设是 ServiceB)的多个方法,而且希望这些方法能够异步执行。

    [1]开启异步功能

    在主启动类使用 @EnableAsync 注解:

    // 开启支持异步方法调用功能
    @EnableAsync
    @SpringBootApplication
    public class Application {
    
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    
    }

    [2]标记异步方法

    在想要异步调用的方法上使用 @Async 注解:

    @Service
    public class DemoService {
    
        // 在想要实现异步调用的方法上加 @Async注解
        @Async
        public void doSth() {
            System.out.println("Demo Service " + Thread.currentThread().getName());
        }
    
    }

    展开全文
  • 线程创建的三种方式区别

    千次阅读 2021-07-13 11:12:30
    创建方式 1. 继承Thread类 定义Thread类的子类,并重写该类的run方法。 创建Thread子类的实例,即创建了线程对象。 调用线程对象的start()方法来启动线程。 package Thread; /* 1.继承Thread类 2.重写run()...
  • java多线程三种方式区别 1)实现Runnable接口  定义线程类,实现Runnable接口,重写其public void run(),将此类的对象当做Thread类的构造函数中的参数  所有子线程公用一套run中代码 2)继承Thread...
  • 线程三种创建方式以及区别

    千次阅读 2021-03-07 18:10:39
    这里写目录标题一:创建线程三种方式1.继承类Thread的方式2.继承Runnable接口方式3.使用Callable接口创建线程4.这三种创建线程区别 一:创建线程三种方式 1.继承类Thread的方式 注意:这种创建线程方式不能再...
  • 【java多线程编程】三种多线程的实现方式

    万次阅读 多人点赞 2019-01-01 16:20:56
    文章目录前言进程与线程继承Thread类,实现多线程FAQ 为什么多线程的启动不直接使用run()方法而必须使用Thread类中start()方法呢?基于Runnable接口实现多线程Thread 与 Runnable 的关系Callable实现多线程线程...
  • 多线程的几创建方式

    千次阅读 2020-04-08 14:37:04
    先说下线程的几状态(5): 新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread(); 就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪...
  • 实现多线程方式 --继承Thread类,重写run函数 --实现Runnable接口 --实现Callable接口三种方式区别 --实现Runnable接口可以避免Java单继承特性而带来的局限;增强程序的健壮性,代码能够被多个线程共享,...
  • java创建多线程的四种方式及其区别

    千次阅读 2019-04-26 18:35:25
    java创建多线程有如下四种方式: 继承Thread类创建线程 实现Runnable接口创建线程 使用Callable和Future创建线程 使用线程池创建(使用java.util.concurrent.Executor接口) 下面分别介绍这四创建多线程方式...
  • 1、采用实现 Runnable、Callable 接口的方式创建多线程。 优势是: 线程类只是实现了 Runnable 接口或 Callable 接口,还可以继承其他类。 在这种方式下,多个线程可以共享同一个 target 对象,所以非常适合多...
  • Java 实现多线程的四种方式

    万次阅读 2020-09-19 22:06:34
    在 Java 中实现多线程一共有四种方式: 继承 Thread 类 实现 Runnable 接口 实现 Callable 接口 线程池 下面我将对这四种方式进行入门级的解析和演示。 一、继承 Thread 类 通过继承 Thread 类实现多线程的步骤...
  • Qt的4种多线程实现方式

    千次阅读 多人点赞 2020-09-17 10:59:24
    一、实现方法:新建一个集成QThread的类,重写虚函数run,通过run启动线程 二、示例: #include <QThread> #include <QDebug> class MyThread : public QThread { Q_OBJECT protected: void run(){ ...
  • 开启多线程方式 实现多线程方式一:继承Thread类 方法介绍 方法名 说明 void run() 在线程开启后,此方法将被调用执行 void start() 使此线程开始执行,Java虚拟机会调用run方法() 实现步骤 ​ ...
  • 线程三种创建方式

    千次阅读 2021-07-17 20:39:28
    文章目录 前言 线程的创建 方法一:继承Thread 方法二:实现Runnable接口 方法:实现Callable接口 区别 总结 前言 本文主要介绍线程三种创建方式线程的创建 方法一:继承Thread (1)先创建一个类,继承java....
  • iOS多线程不同方式简单使用

    千次阅读 2015-11-09 09:28:48
    iOS多线程不同方式简单使用: 1、performSelector.... 2、NSThread 3、NSOperation 4、gcd
  • 创建多线程三种方式以及优缺点

    千次阅读 2018-06-22 11:21:13
    1、继承Thread类创建线程类(1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。(2)创建Thread子类的实例,即创建了线程对象。(3)调用线程...
  • 实现多线程有两种方式及其区别

    千次阅读 2017-09-28 19:51:34
    实现多线程有两种方式:(自JDK1.5之后有三种,最后一并不常用)  1.继承Thread类  2.实现Runnable接口(Callable接口)   一个类如果实现了Runnable接口或者继承了Thread类,那么它就是一个多线程类,如果是...
  • 一、创建线程方式 方式一:继承Thread类 1)① 新建一个类,这个类继承了Thread,此时这个实现类就是一个线程类了。② 在线程类中重写Thread类的run()方法,重写的run方法中是线程类要执行的代码。③ 当要开启新...
  • 当使用多线程访问同一个资源的时候,非常容易出现线程安全的问题(例如,当多个线程同时对一个数据进行修改的时候,会导致某些线程对数据的修改丢失)。 因此,需要采用同步机制来解决这种问题。而Java主要提供了...
  • 线程的实现方式一:继承Thread 步骤: 1.继承Thread,创建Thread类的对象,封装一个独立的线程; 2.重写run方法,封装线程任务; 3.创建线程对象,启动线程; 继承Thread在实际开发中很少用。 线程的实现方式二:...
  • 三种创建线程的方法分别在上述文章中已介绍,分别为:CreateThread,AfxBeginThread,_beginthread/beginthreadex 区别: CreateThread是Windows API函数,提供操作系统级别操作,不用于MFC及RTL函数中。
  • Android多线程实现方式包括: 二、基础使用 Android多线程实现的基础使用包括: 继承Thread类 实现Runnable接口 Handler 接下来对各个进行分析。 1、继承Thread类 1.1 简介 1.2 使用详解 (1)使用步骤 (2) ...
  • 多线程的实现方式有如下三种 1)继承Thread类创建线程 重写run方法 2)实现Runnable接口创建线程 3)使用Callable和Future创建线程 重写call方法 通过继承Thread类来创建并启动多线程的一般步骤如下 1. 定义Thread类...
  • java 创建线程三种方式、创建线程池的四种方式

    万次阅读 多人点赞 2019-02-23 21:01:44
    java创建线程三种方式: 继承Thread类创建线程类 实现Runnable接口 通过Callable和Future创建线程 java创建线程池的四种方式: newCachedThreadPool 创建一个可缓存的线程池,如果线程池长度超过处理...
  • 第一 继承Thread类 ...使用继承Thread类来实现多线程时,最大的局限是不能支持多线程,因为Java语言的特点是单根继承。 创建过程首先新建一个类继承Thread类,然后重写run方法,在run方法中,写线程要执行的任...
  • java中实现多线程的两方法

    千次阅读 2021-03-09 07:44:52
    java多线程有几实现方法,都是什么?同步有几实java中多线程的实现方法有两:1.直接继承thread类;2.实现runnable接口;...JAVA多线程有哪几实现方式JAVA多线程实现方式主要有三种:继承Thread类、实现Ru...
  • 多线程高并发编程】二 实现多线程的几种方式

    万次阅读 多人点赞 2020-02-17 23:32:54
    本文我们来看看多线程的应用场景,为什么要用多线程,以及实现一个多线程有几种方式
  • java有以下四创建多线程方式: 1:继承Thread类创建线程 2:实现Runnable接口创建线程 3:使用Callable和FutureTask创建线程 4:使用线程池,例如用Executor框架创建线程 DEMO代码: package thread; ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 428,507
精华内容 171,402
关键字:

多线程三种方式区别