• Java并发编程之线程创建和启动(Thread、Runnable、Callable和Future)


    这一系列的文章暂不涉及Java多线程开发中的底层原理以及JMM、JVM部分的解析(将另文总结),主要关注实际编码中Java并发编程的核心知识点和应知应会部分。

    说在前面,Java并发编程的实质,是线程对象调用start方法启动多线程,而线程对象则必须是Thread类或其子类实现。Runnable和Callable的作用类似于Comparable、Serializable,是用于被并发的类实现的接口,从而使得Thread类可以在初始化时传入这个被并发的类。此是大前提。本文从多线程实现和启动出发,对这些类或接口予以说明。

    Thread

    通过Thread的子类创建多线程的步骤如下:

    1. 创建Thread的子类,并重写run()方法,该方法即为线程执行体。

    2. 创建Thread子类的对象,即为线程对象。

    3. 调用线程对象的start()方法启动线程。

     1 public class TestThread extends Thread{
     2     
     3     public TestThread(String name) {
     4         setName(name);
     5     }    
     6     @Override
     7     public void run() {
     8         while(!interrupted())
     9             System.out.println(getName() + "线程执行了");
    10     }    
    11     public static void main(String[] args) {
    12         
    13         TestThread t1 = new TestThread("first");
    14         TestThread t2 = new TestThread("second");
    15         //setDaemon()设置线程为守护线程
    16 //        t1.setDaemon(true);
    17 //        t2.setDaemon(true);        
    18         t1.start();
    19         t2.start();        
    20         t1.interrupt();
    21     }
    22 }

    Runnable

    需要并发执行的类,可以通过实现Runnable接口,作为Thread的Target来创建线程对象。

     1 public class TestRunnable implements Runnable{
     2 
     3     @Override
     4     public void run() {
     5         while(true) {
     6             System.out.println("thread running...");
     7             try {
     8                 Thread.sleep(1000);
     9             } catch (InterruptedException e) {
    10                 e.printStackTrace();
    11             }
    12         }
    13     }
    14     
    15     public static void main(String[] args) {
    16         //传入TestRunnable对象作为Target, 开启线程
    17         Thread t = new Thread(new TestRunnable());
    18         t.start();
    19         //采用匿名内部类的方式创建和启动线程
    20         new Thread() {
    21             @Override
    22             public void run() {
    23                 System.out.println("Thread的匿名内部类");
    24             }
    25         }.start();
    26         //父类采用匿名实现Runnable接口, 并由子类继承
    27         new Thread(new Runnable() {
    28             
    29             @Override
    30             public void run() {
    31                 System.out.println("父类的线程");
    32             }
    33         }) {
    34             @Override
    35             public void run() {
    36                 System.out.println("子类的线程");
    37             }
    38         }.start();        
    39     }
    40 }

    Callable和Future

    Java5开始提供了Callable接口,用于现有多线程开发的强力补充。Callable接口提供一个call()方法来构造线程执行体。

    1. call()方法可以有返回值

    2. call()方法可以声明抛出异常

    因此Callable接口没有继承Runnable接口,不能直接作为Thread类的Target来构造线程对象,所以Java5提供了Future接口来代表call方法的返回值。

    Future提供了FutureTask实现类,该实现类实现了Future接口和Runnable接口,像桥梁一样把线程执行体和线程对象连接了起来。

    Future接口提供了若干公共方法来操作Callable任务:

    • boolean cancel(boolean mayInterruptIfRunning): 试图取消Future里关联的Callable任务
    • V get():返回Callable任务里call方法的返回值。调用该方法会导致阻塞,必须等子线程完成后才得到返回值
    • V get(long timeout, TimeUnit unit):最多阻塞timeout和unit指定的时间,超时将抛出TimeoutException异常
    • boolean isCancelled():Callable任务正常完成前被取消,则返回true
    • boolean isDone():Callable任务已完成,则返回true

    创建并启动有返回值的线程步骤如下:

    1. 创建Callable接口的实现类,并实现call方法作为线程执行体,再创建类的实例。Java8中可通过Lambda表达式进行。

    2. 使用FutureTask类来包装Callable实现类的对象

    3. 使用FutureTask作为Thread对象的target

    4. 使用FutureTask对象的get方法获取子线程执行后的返回值

    Callable接口和FutureTask实现类的底层是基于接口回调技术实现,具体可参考:基于接口回调详解JUC中Callable和FutureTask实现原理

     1 public class TestCallable implements Callable<Integer>{
     2     //实现Callable并重写call方法作为线程执行体, 并设置返回值1
     3     @Override
     4     public Integer call() throws Exception {
     5         System.out.println("Thread is running...");
     6         Thread.sleep(3000);
     7         return 1;
     8     }
     9     
    10     public static void main(String[] args) throws InterruptedException, ExecutionException {
    11         //创建Callable实现类的对象
    12         TestCallable tc = new TestCallable();
    13         //创建FutureTask类的对象
    14         FutureTask<Integer> task = new FutureTask<>(tc);
    15         //把FutureTask实现类对象作为target,通过Thread类对象启动线程
    16         new Thread(task).start();    
    17         System.out.println("do something else...");
    18         //通过get方法获取返回值
    19         Integer integer = task.get();    
    20         System.out.println("The thread running result is :" + integer);    
    21     }
    22 }

    总结一下,虽然继承Thread类的开发方式相对简单,但因为Java单继承的限制,一般建议通过实现Runnable或Callable接口来创建并启动多线程。

  • 相关阅读:
    Divide Two Integers
    LRU Cache
    Max Points on a Line
    Recover Binary Search Tree
    Copy List with Random Pointer
    IEE修改最大连接数
    SQL Tuning 基础概述02
    RHEL 本地yum源配置
    Linux演示 dd测试IO
    Linux平台oracle 11g单实例 安装部署配置 快速参考
  • 原文地址:https://www.cnblogs.com/leoliu168/p/9914344.html
Copyright © 2020-2023  润新知