并发编程-基础篇[实现线程的三种方式]

实现线程的方式:

Java中线程的实现有三种方式,分别是继承Thread类、实现Runnable接口和实现Callable接口。
java中实现多线程的方式有三种,接下来我将会逐个进行介绍。

一、Java 多线程实现方式

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

class TestThread extends Thread{
       
    private String name;
 
    public TestThread (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的类
        TestThread thread1 = new TestThread ("Thread1");
        //通过从Thread类中所继承的start()方法启动线程;
        thread1.start();    
 
    }
 
}
2.实现Runable接口

接下来请看这样一种场景,DogRobot是一个用来看门的Dog,现在需要多个Dog分别把守不同的位置,但DogRobot一定要继承Robot父类,此时应该如何实现多线程呢?
在这种场景下,可以通过实现Runable接口的方式来实现一个类的多线程。
我们知道,在Java中,类的继承是单一的,即一个子类仅可以继承一个父类(一个儿子只有一个爹,符合自然规律),但可以实现多个接口。那么,通过Runable接口来实现多线程的好处自然不言而喻。
接下来看一下实现方式:

public class threadLearn {
   
    public static void main(String[] args)  {
        //实例化继承了Thread的类
        TestThread thread1 = new TestThread ("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;
       }
       
}
3. 使用Executor框架实现多线程

在介绍Excutor实现多线程之前,我们先来学习另外一种多线程的实现方式,即继承Callable接口实现多线程的方式。
首先我们来看一下Callable的接口:
并发编程-基础篇[实现线程的三种方式]_第1张图片
可以看到,Callable的接口只有一个方法,那么我们在实现这个接口的时候也仅需要实现这个方法即可。

/*
 * Callable接口实现多线程Demo
 */
class  MyCallable 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}.
     *
     * @param  callable 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();
    }

并发编程-基础篇[实现线程的三种方式]_第2张图片
到这里,一个通过Callable实现的接口便是成功了。

那么这里会有一个问题,按照这样的实现方式,那和Runnable接口有什么区别???
其实我们应该注意到了,Callable接口里的call方法,是一个有返回值的方法;
且FutureTask类的实现方式中,针对Runnable的实现方式,也是携带有一个参数result,由result和Runnable实例去合并成一个Callable的实例。
实现了Callable接口的线程,是具有返回值的。而对于一些对线程要求有返回值的场景,是非常适用的。

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

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

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

ExecutorService 接口
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 的实现类。
这个线程池类里定义了各种我们需要对线程池进行操作的方法。后续有时间我们再研究一下源码。

你可能感兴趣的:(juc)