java多线程---集成接口实现多线程(二)

版权声明:白小姐 https://blog.csdn.net/bailerong123/article/details/88874215


继承Runnable接口和Callable接口实现多线程

Runnable接口实现多线程

Thread类的核心功能是进行线程的启动。如果一个类为了实现一个多线程直接去继承Thread类就会有但继承局限。在java中又提供另外一种实现模式:Runnable接口。
观察Runnable接口:

@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

范例:利用Runnable接口实现线程主题类

package ThreadImpl;

class MyThread1 implements Runnable{
    private String title;
    public MyThread1(String title) {
        this.title = title;
    }
    @Override
    public void run() {
        for(int i=0;i<10;i++){
            System.out.println(this.title+"、i="+i);
        }
    }
}

我们这样实现我们的接口之后,我们的问题就出现了。此时我们的MyThread1类不再是继承Thread实现,而是通过实现我们的Runnable接口实现,虽然解决了单继承局限问题,但是没有我们的start()方法被继承了。那么此时就需要关注Thread类提供的构造方法。
Thread类提供的构造方法

public Thread(Runnable target)

可以接受我们的Runnable接口对象。
启动我们的多线程:

public class TestRunnableTest {
    public static void main(String[] args) {

        MyThread1 mt1=new MyThread1("Thread1");
        MyThread1 mt2=new MyThread1("Thread2");
        MyThread1 mt3=new MyThread1("Thread3");
        new Thread(mt1).start();
        new Thread(mt2).start();
        new Thread(mt3).start();
    }
}

这个时候就启动了多线程,多线程的启动永远都是Thread类的start()方法
这个时候需要注意的是,对于此时的Runnable接口对象可以采用匿名内部类或者是Lambda表达式来定义。
范例:使用匿名内部类进行Runnable对象创建

public class TestRunnableTest {
    public static void main(String[] args) {
       new Thread(new Runnable() {
           @Override
           public void run() {
               System.out.println("hello word");
           }
       }).start();
    }
}

使用Lamdba表达式进行Runnable对象创建

public class TestRunnableTest {
    public static void main(String[] args) {
     Runnable runnable=()-> System.out.println("hello world");
     new Thread(runnable).start();

    }
}

在实际开发中,我们大多数情况下是采用上面两种操作进行Runnable对象的创建

Thread与Runnable区别

首先从使用形式上来看,明显使用我们的Runnable实现多线程要比继承Thread类来的好,因为可以避免单继承的局限性
除了这点之外,我们的Thread类和Runnable接口之间还有下面的区别:
来看我们的Thread类的定义

public class Thread implements Runnable

Thread类是Runnable接口的子类,那么Thread类一定覆写了Runnable接口的run()方法


在多线程的处理上面使用的就是代理设计模式。除了以上的关系之外,实际上在开发之中使用Runnable还有一个特点:使用Runnable实现多线程的程序类可以更好的描述出程序共享的概念
范例:使用Thread实现数据共享(产生若干线程进行同一数据的处理操作)

package Thread;

class MyThread extends Thread{
    private int ticket=10;

    @Override
    public void run() {
        while(this.ticket>0){
            System.out.println("剩余票数"+this.ticket--);

        }
    }
}
public class TestThread {
    public static void main(String[] args) {
        new MyThread().start();
        new MyThread().start();
        new MyThread().start();
    }
}

运行结果:

剩余票数10
剩余票数9
剩余票数8
剩余票数7
剩余票数6
剩余票数5
剩余票数4
剩余票数3
剩余票数2
剩余票数10
剩余票数10
剩余票数9
剩余票数1
剩余票数8
剩余票数7
剩余票数6
剩余票数5
剩余票数4
剩余票数9
剩余票数8
剩余票数7
剩余票数3
剩余票数6
剩余票数2
剩余票数5
剩余票数1
剩余票数4
剩余票数3
剩余票数2
剩余票数1

Process finished with exit code 0

我们会发现,我们通过继承Thread类实现的多线程不是我们的心中所想象的那样的,我们本身想的是启动三个线程共同卖这些票,可是最后的结果却是我们的线程各自卖各自的票
我们现在通过我们的Runnable实现共享:

package Thread;
class MyThread1 implements  Runnable{
    private  int ticket=10;

    @Override
    public void run() {
    while(this.ticket>0){
        System.out.println("剩余票数"+this.ticket--);
    }
    }
}
public class TestRunnable {
    public static void main(String[] args) {
     MyThread1 mt=new MyThread1();
     new Thread(mt).start();
     new Thread(mt).start();
    }
}

我们的运行结果:

剩余票数10
剩余票数8
剩余票数7
剩余票数6
剩余票数5
剩余票数4
剩余票数3
剩余票数2
剩余票数9
剩余票数1

Process finished with exit code 0

我们发现,我们的运行结果虽然还不是我们所期待的那个样子,但是已经有了大概的眉目
所以使用我们的Runnable实现的多线程的程序类可以更好的描述程序共享的概念。

Runnable接口的继承关系图

在这里插入图片描述

Callable实现多线程

从JDK1.5开始追加了新的开发包:java.util.concurrent.这个开发包最主要是进行高并发变成使用,包含很多在高并发操作中会使用的类。在这个包里定义了一个新的接口Callable
Runnable中的run()方法没有返回值,它的设计也遵循了主方法的设计原则:线程开始了就别回头。但是在很多时候需要一些返回值,例如某些县城执行完成后可能带hi一些返回结构,这种情况下就只能利用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;
}

我们现在使用Callable来定义以下我们的线程主体类

class MyThread implements Callable{
    private int ticket=10;
    @Override
    public Object call() throws Exception {
        while(this.ticket>0){
            System.out.println("剩余票数"+this.ticket--);
        }
        return "票完了";

    }
}

我们进行启动并取得我们的多线程的执行结果:

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<String> task=new FutureTask<>(new MyThread());
        new Thread(task).start();
        new Thread(task).start();
        System.out.println(task.get());
    }
}

我们看一下我们的运行结果:

剩余票数10
剩余票数9
剩余票数8
剩余票数7
剩余票数6
剩余票数5
剩余票数4
剩余票数3
剩余票数2
剩余票数1
票完了

Process finished with exit code 0

我们以上的形式主要就是为了我们的取得线程的执行结果。

Callable接口的继承关系图

这是我们源码中的一些继承

public class Thread implements Runnable 
public class FutureTask<V> implements RunnableFuture<V> 
public interface RunnableFuture<V> extends Runnable, Future<V> 

在这里插入图片描述
我们会发现我么的呢,Future接口里面有我们的get方法,可以帮助我们得到我们的最后的返回值。

猜你喜欢

转载自blog.csdn.net/bailerong123/article/details/88874215
今日推荐