第⼆章 Java多线程——入门类和接口

2.1 Thread类和Runnable接口

2.1.1 继承Tread类

首先是继承Tread类:

/**
* @author :ls
* @date :Created in 2022/4/18 15:10
* @description:
*/
public class T1 {
    public static class MyThread extends Thread{
        @Override
        public void run() {
            System.out.println("MyThread!");
        }
    }


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

注意要调用 start() 方法后,该线程再算启动。

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

我们可以使用java8函数式编程来简化一下代码:

/**
* @author :ls
* @date :Created in 2022/4/18 15:10
* @description:
*/
public class T1 {
    public static class MyThread implements Runnable {
        @Override
        public void run() {
            System.out.println("MyThread");
        }
    }
    public static void main(String[] args) {
        new Thread(new MyThread()).start();

        // Java 8 函数式编程,可以省略MyThread类
        new Thread(() -> {
            System.out.println("Java 8 匿名内部类");
        }).start();
    }
}

2.1.3 Thread类构造方法

Thread类是一个 Runnable 接口的实现类
查看Thread类的构造方法,发现其实是简单调用一个私有的init 方法来实现初始化的。

//Thread类源码

//片段1  init方法
private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize, AccessControlContext acc,
                  boolean inheritThreadLocals)

//片段二  构造函数
public Thread(Runnable target) {
    init(null, target, "Thread-" + nextThreadNum(), 0);
}

//片段三  在init方法中初始化的 AccessControlContext
this.inheritedAccessControlContext =
        acc != null ? acc : AccessController.getContext();

//片段四  两个支持ThreadLocal的私有属性
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

解释一下 这些个参数:

  • g : 线程组,指定这个线程是在哪个线程组下。
  • target : 指定要执行的任务。
  • name : 线程的名字,多个线程的名字是可以重复的。如不指定名字,见片段二。
  • acc : 见片段三,用于初始化私有变量inheritedAccessControlContext。
  • inheritThreadLocals : 可继承的 ThreadLocal ,见片段4,Thread类里面有量的私有属性来支持ThreadLocal。

一般来说我们用的最多的是:

Thread(Runnable target) 
Thread(Runnable target, String name)

2.1.3 Thread类的几个常用方法

  • currentThread() : 静态方法,返回对当前正在执行的线程对象的引用。
  • start() : 开始执行线程的方法,java虚拟机会调用线程内部的run()方法。
  • yield() : 放弃当前线程占用的cup资源。这里需要注意的是,就算当前线程调用了yield()方法,程序在调度的时候,也还有可能继续运行这个线程。
  • sleep() : 静态方法,使用当前线程睡眠一段时间。
  • join() : 使用当前线程等待另一个线程执行完毕之后在继续执行,内部调用是Object类的wait()方法实现的。

2.1.4 Thread类与Runnable接口的比较

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

2.2 Callable、Future与FutureTask

2.2.1 Callbale接口

Callable与Runnable类似,同样是只有一个抽象方法的函数式接口。不同的是,callable提供的方法是有返回值的,而且支持泛型。

@FunctionalInterface 
public interface Callable { 
    V call() throws Exception; 
}

callable一般是配合线程池工具 ExecutorService 来使用的。通过 submit 方法来让一个 Callable接口执行。它会返回一个 Future ,通过 Future 的 get 方法可以得到返回的结果。

/**
* @author :ls
* @date :Created in 2022/4/18 22:46
* @description:
*/
public class T2 implements Callable<Integer>{
        @Override
        public Integer call() throws Exception {
            // 模拟计算需要⼀秒
            Thread.sleep(1000);
            return 2;
        }
        public static void main(String args[]) throws ExecutionException, InterruptedException {
            // 使⽤
            ExecutorService executor = Executors.newCachedThreadPool();
            T2 task = new T2();
            Future<Integer> result = executor.submit(task);
            // 注意调⽤get⽅法会阻塞当前线程,直到得到结果。
            // 所以实际编码中建议使⽤可以设置超时时间的重载get⽅法。
            System.out.println(result.get());
        }
}

输出结果:

2

2.2.2 Future 接口

public abstract interface Future<V> {
    public abstract boolean cancel(boolean mayInterruptIfRunning);
    public abstract boolean isCancelled();
    public abstract boolean isDone();
    public abstract V get() throws InterruptedException, ExecutionException;
    public abstract V get(long timeout, TimeUnit unit)
         throws InterruptedException, ExecutionException, TimeoutException;
}

cancel 方法是试图取消一个线程的执行。 注意这里是试图取消并不一定成功。因为任务可能已完成、或者一些其他因素不能取消,存在取消失败可能。
返回值 表示“是否取消成功”
参数 mayInterruptIfRunning 表示是否采用中断的方式取消线程。

因此为了让任务有能够取消的功能,就使用callable来代替runnable。如果为了可取消性⽽使⽤ Future 但⼜不提供可⽤的结果,则可以声明 Future<?> 形式类型、并返回 null 作为底层任务的结果。

2.2.3 FutureTask类

这个接口有一个实现类叫 FutureTask 。 FutureTask 是 实现的 RunnableFuture 接口的,而 RunnableFuture 接口同时继承了 Runnable 接口 和 Future 接口:

public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
    void run();
}

Future 只是一个接口,而它里面的 cancel , get , isDone 等方法要自己实现 起来都是非常复杂的。所以JDK提供了一个 FutureTask 类来供我们使用。

/**
* @author :ls
* @date :Created in 2022/4/18 23:15
* @description:
*/
public class T3 implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        // 模拟计算需要⼀秒
        Thread.sleep(1000);
        return 2;
    }
    public static void main(String args[]) throws ExecutionException, InterruptedException {
        // 使⽤
        ExecutorService executor = Executors.newCachedThreadPool();
        FutureTask<Integer> futureTask = new FutureTask<>(new T3());
        Future<?> submit = executor.submit(futureTask);
        System.out.println(futureTask.get());
    }
}

使用上与第一个Demo有一点小的区别。首先,调用 submit 方法是没有返回值的。 这里实际上是调用的 submit(Runnable task) 方法,而上面的Demo,调用的 是 submit(Callable task) 方法。

然后,这里是使用 FutureTask 直接取 get 取值,而上面的Demo是通过 submit 方 法返回的 Future 去取值。

在很多高并发的环境下,有可能Callable和FutureTask会创建多次。FutureTask能 够在高并发环境下确保任务只执行一次。

2.2.3 FutureTask的几个状态

/** 
  * state可能的状态转变路径如下: 
  * NEW -> COMPLETING -> NORMAL 
  * NEW -> COMPLETING -> EXCEPTIONAL 
  * NEW -> CANCELLED 
  * NEW -> INTERRUPTING -> INTERRUPTED 
  */

private volatile int state;   //任务的运⾏状态
private static final int NEW          = 0;    //初始状态为NEW
private static final int COMPLETING   = 1;    //完成状态/结束
private static final int NORMAL       = 2;    //正常的
private static final int EXCEPTIONAL  = 3;    //异常的
private static final int CANCELLED    = 4;    //已取消
private static final int INTERRUPTING = 5;    //打断
private static final int INTERRUPTED  = 6;    //已中断
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值