多线程基础(2)-入门类和接口

2.1 Thread类和Runnable接口

那么在Java中,我们是如何使用多线程的呢?

首先,我们需要有一个“线程”类。JDK提供了Thread类和Runnable接口来让我们实现自己的“线程”类。

  • 继承Thread类,并重写run方法;
  • 实现Runnable接口的run方法;

2.1.1 继承Thread类

  Thread的结构比较复杂

public class UsingThread {

    public static void main(String[] args){
        new MyThread().start();//继承Thread类
    }

    public static class MyThread extends Thread{
        @Override
        public void run(){
            System.out.println("MyThread");
        }
    }

}

2.1.2 实现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();
}
public class UsingThread {

    public static void main(String[] args){
        new Thread(new MyRunnable()).start();//实现Runnable接口,代理模式,需要Thread来执行
    }
    public static class MyRunnable implements Runnable{
        @Override
        public void run() {
            System.out.println("MyRunnable");
        }
    }
}

 

2.1.3 Thread类与Runnable接口的比较:

实现一个自定义的线程类,可以有继承Thread类或者实现Runnable接口这两种方式,它们之间有什么优劣呢?

  • 由于Java“单继承,多实现”的特性,Runnable接口使用起来比Thread更灵活
  • Runnable接口出现更符合面向对象,将线程单独进行对象的封装
  • Runnable接口出现,降低了线程对象和线程任务的耦合性
  • 如果使用线程时不需要使用Thread类的诸多方法,显然使用Runnable接口更为轻量

PS:个人总结,实现Runnable接口,再通过Thread代理执行,这样解耦,“线程任务“”和“线程对象“”解耦。

2.1.4 Thread类 start方法解析

通过源码可以发现start()方法调用的native start0()方法

   public synchronized void start() {
        if (threadStatus != 0)//状态不是新建的话,抛出非法线程状态异常,调用启动后,状态不为0
            throw new IllegalThreadStateException();
        group.add(this);
        boolean started = false;
        try {
            start0();//调用
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

  

2.1.5 Thread类 setDaemon方法

  Daemon,守护;setDaemon设置为守护线程,在实例调用start方法之前可以调用,来转换为守护线程。

  守护线程是一种用来提供通用服务的线程,为用户线程服务。当只有守护线程的时候,JVM会退出。JVM必须等待所有的用户线程执行完毕才能退出。

  守护线程的子线程是守护线程,因为守护线程不会访问或者写入任何的资源,守护线程会中任何的时刻被终止,不会保证执行完毕。

//Marks this thread as either a {@linkplain #isDaemon daemon} thread
//or a user thread. The Java Virtual Machine exits when the only
//threads running are all daemon threads.
//throws IllegalThreadStatException if this thread is alive   //将线程转换为另一种线程,守护线程。当只有守护线程的时候,JVM会退出。 public final void setDaemon(boolean on) { checkAccess(); if (isAlive()) { throw new IllegalThreadStateException(); } daemon = on; }

  

   /**
     * Tests if this thread is alive. A thread is alive if it has
     * been started and has not yet died.
     *
     * @return  <code>true</code> if this thread is alive;
     *          <code>false</code> otherwise.
     */
    public final native boolean isAlive();

  

 

2.2 Callable、Future与FutureTask

  通常来说,我们使用RunnableThread来创建一个新的线程。但是它们有一个弊端,就是run方法是没有返回值的。而有时候我们希望开启一个线程去执行一个任务,并且这个任务执行完成后有一个返回值。JDK提供了Callable接口与Future接口为我们解决这个问题,这也是所谓的“异步”模型。

@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的呢?Callable一般是配合线程池工具ExecutorService(FutrueTask实现RunnableFutrue接口,这个接口继承Runnable接口,故可以提交到线程池执行)来使用的。我们会在后续章节解释线程池的使用。这里只介绍ExecutorService可以使用submit方法来让一个Callable接口执行。它会返回一个Future,我们后续的程序可以通过这个Futureget方法得到结果。

  

public class Callable2Runnable {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();//java.util.concurrent
        MyCallable callableTask = new MyCallable();
        Future<String> callableFuture = executorService.submit(callableTask);//方法实现:将callable对象转换为FutureTask对象,FutrueTask实现RunnableFutrue接口,
     //RunnableFuture继承Runnable接口 callableFuture.cancel(true); System.out.println(callableFuture.isCancelled());//future线程执行结果 System.out.println(callableFuture.get());//future线程执行结果 MyRunnable runnableTask = new MyRunnable(); Future runnableFuture = executorService.submit(runnableTask);//submit也可以 executorService.execute(runnableTask);//可以通过execute方法执行 FutureTask<String> futureTask = new FutureTask<>(callableTask);//将runnable或者callable通过FutureTask来执行 executorService.submit(futureTask); System.out.println(futureTask.get()); } public static class MyRunnable implements Runnable{ @Override public void run() {//void方法 System.out.println("MyRunnable"); } } public static class MyCallable implements Callable<String>{ @Override public String call() throws Exception {//异常可以往外抛,有返回值 return "MyCallable"; } } }

  将callable对象转换为FutureTask对象,FutrreTask实现了RunnableFuture接口,RunnableFuture接口继承类Runnable接口,所以可以提交到线程池来执行。

   public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }

  

  

 

 

 

 

 总结下:

     

 

 

      原文地址:http://redspider.group:4000/article/01/2.html

   感恩作者

 

posted @ 2020-11-21 09:01  knbsyoo  阅读(137)  评论(0)    收藏  举报