Thread、Runnable、FutureTask和Callable以及线程池创建线程

一、线程的创建方式

1、继承Thread类

(1)创建一个继承Thread的子类,并重写run方法:

@Slf4j //需要安装lombok
class MyThread extends Thread{
    @Override
    public void run(){
        log.info("继承Thread创建线程");
    }
}

(2)创建Thread的子类对象,并调用start方法启动线程:

@Slf4j
public class ThreadTest {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
        log.info("主线程");
    }
}

输出:

10:48:04.033 [Thread-0] INFO com.demo.MyThread - 继承Thread创建线程
10:48:04.033 [main] INFO com.demo.ThreadTest - 主线程

2、实现Runnable接口

(1)创建实现Runnable接口的类,实现抽象run方法:

@Slf4j
class MyThread implements Runnable{
    @Override
    public void run(){
        log.info("继承Thread创建线程");
    }
}

(2)创建实现Runnable接口类的对象,将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象,然后调用start方法:

@Slf4j
public class ThreadTest {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread thread = new Thread(myThread);
        thread.start();
        log.info("主线程");
    }
}

(3)使用匿名内部类的方式:

@Slf4j
public class ThreadTest {
    public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                log.info("匿名内部类的方式");
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
        log.info("主线程");
    }
}

(4)使用lambda表达式简写:

@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();
}

上面是Runable接口,里面只有一个抽象方法,被注解FunctionalInterface标记,这时候就可以使用lambda表达:

@Slf4j
public class ThreadTest {
    public static void main(String[] args) {
        Runnable runnable = () -> log.info("lambda表达式的方式");
        Thread thread = new Thread(runnable);
        thread.start();
        log.info("主线程");
    }
}

或者这样写:

@Slf4j
public class ThreadTest {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> log.info("lambda表达式的方式"));
        thread.start();
        log.info("主线程");
    }
}

甚至可以这样:

@Slf4j
public class ThreadTest {
    public static void main(String[] args) {
        new Thread(() -> log.info("lambda表达式的方式")).start();
        log.info("主线程");
    }
}

输出:

10:48:42.618 [main] INFO com.demo.ThreadTest - 主线程
10:48:42.618 [Thread-0] INFO com.demo.ThreadTest - lambda的方式

3、使用FutureTask和Callable创建

  FutureTask能够接受Callable类型的参数,用来处理有返回结果的线程,FutureTask实现了RunnableFuture接口,RunnableFuture接口继承Runnable, Future接口,Future提供了一些方法可以获取线程返回值;Callable接口和Runnable接口非常类似,只有一个call方法,但是有返回值并且可以抛出异常,可以把一个线程的返回值传给另一个线程。

@Slf4j
public class ThreadTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<String> task = new FutureTask<>(new Callable<String>() {
            @Override
            public String call() throws Exception {
                log.info("FutureTask创建线程");
                Thread.sleep(1000);
                return "ok";
            }
        });
        Thread thread = new Thread(task);
        thread.start();
        log.info("{}",task.get());
    }
}

当然也可以简化成如下代码:

@Slf4j
public class ThreadTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<String> task = new FutureTask<>(() -> {
            log.info("FutureTask创建线程");
            Thread.sleep(1000);
            return "ok";
        });
        Thread thread = new Thread(task);
        thread.start();
        log.info("{}",task.get());
    }
}

输出:

12:18:12.489 [Thread-0] INFO com.demo.ThreadTest - FutureTask创建线程
12:18:13.492 [main] INFO com.demo.ThreadTest - ok

  当调用get方法时,主线程会阻塞,直到里面的线程执行完才会拿到返回值结束。

4、使用线程池创建线程

  由于Executors框架的提供创建线程池的方法不推荐使用,这里使用ThreadPoolExecutor来创建线程池,线程池的具体参数会在后面的章节介绍,这里只演示如何通过线程池创建线程:

@Slf4j
public class ThreadTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(8, 200, 10L, TimeUnit.SECONDS, new SynchronousQueue());
        threadPoolExecutor.execute(new Runnable() {
            @Override
            public void run() {
                log.info("running...");
            }
        });
        log.info("主线程");
        threadPoolExecutor.shutdown();
    }
}

或者简写

@Slf4j
public class ThreadTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(8, 200, 10L, TimeUnit.SECONDS, new SynchronousQueue());
        threadPoolExecutor.execute(() -> log.info("running..."));
        log.info("主线程");
        threadPoolExecutor.shutdown();
    }
}
10:43:36.105 [pool-1-thread-1] INFO com.demo.ThreadTest - running...
10:43:36.105 [main] INFO com.demo.ThreadTest - 主线程

5、Thread和Runnable的关系和比较

(1)关系
  Thread实现了Runnable接口;使用Runnable接口方式创建线程,实际是把实现Runnable接口的对象实例当做参数传给Thread,跟踪Thread的构造方法(JDK1.8):

	//1、Thread 460行-462行
    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }
    //2、Thread 374行-350行
    private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize) {
    init(g, target, name, stackSize, null);
    }

    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc) {
        ...
        this.target = target; //3、Thread 413行
        ...
    }
    //4、Thread 742行-747行
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

  根据源码可以看到传入的Runnable实例最终还是在Thread的run方法中被调用,而用继承Thread的方式是重写Thread的run方法。
(2)比较
  继承Thread类的创建方式是把线程和任务合并在了一起,而实现Runnable接口的方式是把线程和任务分开了更容易与线程池高级API配合;Java只支持单继承,而可以实现多个接口,所以实现Runnable接口的创建方式更灵活(推荐使用)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值