2019独角兽企业重金招聘Python工程师标准>>>

文章将从以下几个方面介绍:

前言

Runnable 源码分析

Thread 源码分析

Callable 源码分析

Future 源码分析

FutureTask 源码分析

前言

在 Java 中,实现线程的方式主要有以下几种方式:继承 Thread, 实现 Runnable 和实现 Callable 这三种方式;采用哪种方式,主要根据实际情况而定,比如:因为 Java 是单继承,所以如果定义的线程还有其他父类的话,就可以使用实现 Runnable 的方式,如果定义的线程就只有 Thread 一个父类,就可以从用继承 Thread 的方式来声明线程;如果线程执行后需要有返回值,则可以采用实现 Callable 的方式来声明线程。

Runnable

Runnable 的源码如下:

@FunctionalInterface
public interface Runnable {public abstract void run();
}

Runnable 它是一个接口,只有一个 run 方法,当线程在执行的时候,会自动的执行该 run 方法,我们采用实现 Runnable 的方式声明线程的时候,就需要重写该 run 方法;该方式需要使用 Thread 类的 start 方法来启动线程。如下所示:

// 声明线程
class MyThread2 implements Runnable{@Overridepublic void run() {System.out.println("implements Runnable");}
}// 启动线程
new Thread(new MyThread2()).start();输出:implements Runnable

Thread

Thread 类本身就是一个线程,它实现了 Runnable 接口,它提供了很多的方法来控制线程的行为,类图如下:

每个线程都有优先级(priority),高优先级的线程会优于低优先级的线程执行,但并不是说高优先级的线程一定在低优先级的线程之前执行,只是获取到 CPU 的概率要大些。线程的优先级共有 10 个级别,最低级别为1,默认的级别为5,最高级别为10。

当Java虚拟机启动时,通常会有一个非守护程序线程(通常调用某个指定类的main方法)。 当在遇到如下任意情况之前,Java虚拟机会继续执行线程:

1. 调用 Runtime 类的 exit 方法,并且安全管理器允许执行退出操作

2. 所有非守护线程都已“死亡”

3. run 方法执行完毕

4. run 方法抛出异常。

下面来看下 Thread 类的源码,只会选一些常见的进行分析:

public class Thread implements Runnable {// 优先级private int  priority;// 是否单步执行该线程private boolean single_step;// 是否是守护线程,默认不是private boolean daemon = false;// 要运行的线程private Runnable target;// 线程组private ThreadGroup group;// 线程最低的优先级public final static int MIN_PRIORITY = 1;// 线程默认的优先级public final static int NORM_PRIORITY = 5;// 线程最大的优先级public final static int MAX_PRIORITY = 10;// 返回当前线程public static native Thread currentThread();/********************** 常见方法 *******************/}

下面是 Thread 的一些常见方法:

yield()

    /*** A hint to the scheduler that the current thread is willing to yield* its current use of a processor. The scheduler is free to ignore this* hint.** <p> Yield is a heuristic attempt to improve relative progression* between threads that would otherwise over-utilise a CPU. Its use* should be combined with detailed profiling and benchmarking to* ensure that it actually has the desired effect.** <p> It is rarely appropriate to use this method. It may be useful* for debugging or testing purposes, where it may help to reproduce* bugs due to race conditions. It may also be useful when designing* concurrency control constructs such as the ones in the* {@link java.util.concurrent.locks} package.*/public static native void yield();

yield 方法会告诉线程调度器,当前线程愿意放弃CPU的使用权,把CPU让给其他线程执行,当前线程会从执行状态变为可执行状态;但是,调度器可能会忽略该消息,也就是说,yield 方法有意愿放弃CPU的使用权,但是还得看调度器是否同意,即使 yield 已经成功的放弃了CPU的使用权,但是在下一轮调度的时候,还是会调度到它,让它继续执行;yield 方法主要是用来保证其他线程有机会执行而不至于会导致饥饿。一般很少使用该方法,但是它对于调试和测试可能很有用。

测试:

class MyThread1 extends Thread{public MyThread1(String name) {super(name);}@Overridepublic void run() {for (int i = 0; i < 50; i++) {System.out.println(Thread.currentThread().getName() + i);if (i == 30){Thread.yield();}}}
}new MyThread1("thread-1 : ").start();
new MyThread1("thread-2 : ").start();
new MyThread1("thread-3 : ").start();
new MyThread1("thread-4 : ").start();
new MyThread1("thread-5 : ").start();输出:thread-4 : 28
thread-4 : 29
thread-4 : 30 // 此时,thread-4 应该放弃CPU使用权,可是它并没有放弃或者放弃了又再次被调度
thread-4 : 31
thread-4 : 32
........
thread-4 : 49
thread-1 : 0
........
thread-1 : 16
thread-5 : 0
........
thread-5 : 29
thread-5 : 30 // 此时, thread-5 放弃 CPU 的使用权,把机会留给 thread-1 执行
thread-1 : 17
thread-1 : 18
........
thread-1 : 29
thread-1 : 30 // 此时, thread-1 放弃 CPU 的使用权,把机会留给 thread-5 执行
thread-5 : 31

sleep()

    /*** Causes the currently executing thread to sleep (temporarily cease* execution) for the specified number of milliseconds, subject to* the precision and accuracy of system timers and schedulers. The thread* does not lose ownership of any monitors.** @param  millis*         the length of time to sleep in milliseconds* @throws  InterruptedException*          if any thread has interrupted the current thread. The*          <i>interrupted status</i> of the current thread is*          cleared when this exception is thrown.*/public static native void sleep(long millis) throws InterruptedException;

sleep 方法导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数, 该线程不会释放已经拥有的锁。 如果其他的线程中断了一个休眠的线程,sleep方法会抛出Interrupted Exception。

start()

    public synchronized void start() {if (threadStatus != 0)throw new IllegalThreadStateException();group.add(this);boolean started = false;try {start0();started = true;} finally {try {if (!started) {group.threadStartFailed(this);}} catch (Throwable ignore) {}}}private native void start0();

start 方法是用来启动一个线程,当调用 start 方法后,JVM 会自动去执行当前线程的 run 方法,从上述源码中可以看到,start 会执行 start0 方法,而 start0 方法是一个本地方法,run 方法应该在里面调用的吧。

start 方法只能调用一次,多次调用会出错。

interrupt()

    public void interrupt() {if (this != Thread.currentThread())checkAccess();synchronized (blockerLock) {Interruptible b = blocker;if (b != null) {interrupt0(); // Just to set the interrupt flagb.interrupt(this);return;}}interrupt0();}

interrupt 方法中断当前线程。如果在调用 wait 或 join 时阻塞了这个线程,那么它的中断状态将被清除,它将收到一个 InterruptedException。如果此线程在I / O操作中被阻塞,那么通道将关闭,线程的中断状态将被设置,并且线程将接收到 ClosedByInterruptExcetion。如果上述操作没有抛出异常,则将设置该线程的中断状态。

interrupt方法并不是强制终止线程,它只能设置线程的中断状态

interrupted()

    public static boolean interrupted() {return currentThread().isInterrupted(true);}

测试当前线程是否已经中断。线程的中断状态 由该方法清除。换句话说,如果连续两次调用该方法,则第二次调用将返回 false(在第一次调用已清除了其中断状态)

isInterrupted()

    public boolean isInterrupted() {return isInterrupted(false);}

测试线程是否已经中断。线程的中断状态不受该方法的影响。

join()

    public final synchronized void join(long millis)throws InterruptedException {long base = System.currentTimeMillis();long now = 0;if (millis < 0) {throw new IllegalArgumentException("timeout value is negative");}if (millis == 0) {while (isAlive()) {  // 判断线程是否还存活wait(0);}} else {while (isAlive()) {long delay = millis - now;if (delay <= 0) {break;}wait(delay); // 让线程等待指定的毫秒数now = System.currentTimeMillis() - base;}}}

join 方法把指定线程加入到当前线程中执行,可以将两个交替执行的线程合并为顺序执行的线程。比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。

以上就是 Thread 类中的常见方法。

既然说到 sleep 方法,就会想到 Object 的 wait 方法。wait 方法也会是线程暂停执行,直到由 notify 或 notifyAll 进行唤醒。调用 wait 方法后,线程会释放掉锁。

Callable

Callable 也可以用来实现线程,采用 Callable 方式执行线程,我们可以得到线程的一个执行结果,线程的执行结果通过 Future 进行返回;

Callable 和 Runnable  类似,都是为了线程而设计,但是 Runnable 的 run 方法执行线程后不能返回结果,也不能抛出异常;而 Callable 的 call 方法可以有返回值和抛出异常。

先看下它的源码实现:

@FunctionalInterface
public interface Callable<V> {/*** 可以返回结果和抛出异常* @return computed result* @throws Exception if unable to compute a result*/V call() throws Exception;
}

Callable 需要配合 ExecutorService 来进行使用,它提供了一系列的的 submit 来执行:

    <T> Future<T> submit(Callable<T> task);

Future

一个 Future 代表着一个异步计算结果,它提供了一些方法去检查计算是否完成,等待其完成,以及检索计算结果等。接下来看下它的接口声明:

public interface Future<V> {// 取消任务,如果任务已完成,则返回false;// 参数mayInterruptIfRunning 表示是否允许取消正在执行的任务,true表示允许,false表示不允许,任务会继续执行boolean cancel(boolean mayInterruptIfRunning);// 是否取消成功boolean isCancelled();// 任务是否完成boolean isDone();// 返回计算结果,该方法会阻塞一直到任务计算完成V get() throws InterruptedException, ExecutionException;// 在一定时间内返回计算结果,超时则返回nullV get(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException;
}

看下它的一个使用:

    public static void main(String[] args) throws ExecutionException, InterruptedException {ExecutorService executorService = Executors.newFixedThreadPool(1);Future<String> future = executorService.submit(new Callable<String>() {@Overridepublic String call() throws Exception {return "hello";}});String result = future.get();System.out.println(result); // helloexecutorService.shutdown();}

FutureTask

FutureTask 提供了 Future 类的一个基本实现,它的类图如下:

可以看到,FutureTask 还实现了 Runnable 接口,所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。

    public FutureTask(Callable<V> callable) {}public FutureTask(Runnable runnable, V result) {}
    public static void main(String[] args) throws ExecutionException, InterruptedException {ExecutorService executorService = Executors.newFixedThreadPool(1);FutureTask<String> futureTask = new FutureTask<>(new Callable<String>() {@Overridepublic String call() throws Exception {System.out.println("hello");return "hello";}});executorService.submit(futureTask);executorService.shutdown();}

以上就是实现线程的几种方式。

转载于:https://my.oschina.net/mengyuankan/blog/2249979

实现线程的方式,源码分析:Runnable, Thread, Callable, Future, FutureTask相关推荐

  1. Java并发编程实战(chapter_3)(线程池ThreadPoolExecutor源码分析)

    为什么80%的码农都做不了架构师?>>>    这个系列一直没再写,很多原因,中间经历了换工作,熟悉项目,熟悉新团队等等一系列的事情.并发课题对于Java来说是一个又重要又难的一大块 ...

  2. idea 线程内存_Java线程池系列之-Java线程池底层源码分析系列(一)

    课程简介: 课程目标:通过本课程学习,深入理解Java线程池,提升自身技术能力与价值. 适用人群:具有Java多线程基础的人群,希望深入理解线程池底层原理的人群. 课程概述:多线程的异步执行方式,虽然 ...

  3. idea 线程内存_Java线程池系列之-Java线程池底层源码分析系列(二)

    课程简介: 课程目标:通过本课程学习,深入理解Java线程池,提升自身技术能力与价值. 适用人群:具有Java多线程基础的人群,希望深入理解线程池底层原理的人群. 课程概述:多线程的异步执行方式,虽然 ...

  4. 从原理到实现丨手把手教你写一个线程池丨源码分析丨线程池内部组成及优化

    人人都能学会的线程池 手写完整版 1. 线程池的使用场景 2. 线程池的内部组成 3. 线程池优化 [项目实战]从原理到实现丨手把手教你写一个线程池丨源码分析丨线程池内部组成及优化 内容包括:C/C+ ...

  5. 【SA8295P 源码分析】16 - TouchScreen Panel (TP)线程函数 tp_recv_thread() 源码分析

    [SA8295P 源码分析]16 - TouchScreen Panel (TP)线程函数 tp_recv_thread 源码分析 一.TP 线程函数:tp_recv_thread() 二.处理&am ...

  6. Netty源码分析(六)—Future和Promis分析

    Netty源码分析(六)-Future和Promis分析 Future用来在异步执行中获取提前执行的结果 个人主页:tuzhenyu's page 原文地址:Netty源码分析(六)-Future和P ...

  7. JAVA线程池(ThreadPoolExecutor)源码分析

    JAVA5提供了多种类型的线程池,如果你对这些线程池的特点以及类型不太熟悉或者非常熟悉,请帮忙看看这篇文章(顺便帮忙解决里面存在的问题,谢谢!):     http://xtu-xiaoxin.ite ...

  8. threadpoolexecutor创建线程池_线程池ThreadPoolExecutor源码分析

    什么是线程池 创建线程要花费昂贵的资源和时间,如果任务来了才创建那么响应时间会变长,而且一个进程能创建的线程数量有限.为了避免这些问题,在程序启动的时候就创建若干线程来响应出来,它们被称为线程池,里面 ...

  9. 14.QueuedConnection和BlockingQueuedConnection连接方式源码分析

    QT信号槽直连时的时序和信号槽的连接方式已经在前面的文章中分析过了,见https://blog.csdn.net/Master_Cui/article/details/109011425和https: ...

  10. c++ 线程池_JAVA并发编程:线程池ThreadPoolExecutor源码分析

    前面的文章已经详细分析了线程池的工作原理及其基本应用,接下来本文将从底层源码分析一下线程池的执行过程.在看源码的时候,首先带着以下两个问题去仔细阅读.一是线程池如何保证核心线程数不会被销毁,空闲线程数 ...

最新文章

  1. Breakpad查C++崩溃问题
  2. R语言广义加性模型GAMs:可视化每个变量的样条函数、样条函数与变量与目标变量之间的平滑曲线比较、并进行多变量的归一化比较、测试广义线性加性模型GAMs在测试集上的表现(防止过拟合)
  3. Rhino脚本引擎技术介绍
  4. html 如何用图片代替单选按钮,HTML中图像代替提交按钮
  5. 12 组合与继承、CSS Module
  6. CCF201403-5 任务调度(100分题解链接)
  7. poi报表导出4.1.0版本工具类 导出并下载
  8. git常用命令,冲突
  9. 微星小飞机怎么超频?
  10. Android TP驱动分析
  11. js使用高德地图api实现定位,行政区域划分,点击事件,只显示某个省市或区,海量点标记
  12. 操作系统的几种基本类型
  13. php的redis函数
  14. js取整,保留小数位数、四舍五入、科学记数法及去掉数字末尾多余的0
  15. 《混乱的猴子》读书笔记 -- 关于硅谷、创业、Facebook和广告
  16. Bean、BeanDefinition、BeanFactory、FactoryBean
  17. 阿里云windows服务器重置密码并连接远程桌面
  18. 启动nodejs时报错 internal/modules/cjs/loader.js:584的解决办法
  19. 四川大学计算机考研课程表,2019年四川大学研究生教学运行作息时间表.doc
  20. 基于 CentOS-Stream9 构建官方CoreOS COSA 镜像并构建 CoreOS

热门文章

  1. 【证明】两个自变量的二阶线性方程经过可逆变换后方程的类型不会改变
  2. java封装的注意事项_新手学习Java之对象---封装
  3. js设置北京时区_盘一盘 Python 系列特别篇十九之 时区和夏时令
  4. Deciding the Number of Clusterings
  5. Graphical Model(概率图模型)的浅见
  6. 转录组拼接软件Trinity使用安装报错锦集
  7. 【Gym-101908 L】Subway Lines【树上两条路径交】
  8. 【POJ 3281】Dining【最大匹配、拆点】
  9. 【HDU 5965】扫雷【线性递推】
  10. (详细带你分析错误):No property 属性名 found for type 类名,总结了其他解决办法