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()方法启动线程。
public class TestThread extends Thread{ public TestThread(String name) {
setName(name);
}
@Override
public void run() {
while(!interrupted())
System.out.println(getName() + "线程执行了");
}
public static void main(String[] args) { TestThread t1 = new TestThread("first");
TestThread t2 = new TestThread("second");
//setDaemon()设置线程为守护线程
// t1.setDaemon(true);
// t2.setDaemon(true);
t1.start();
t2.start();
t1.interrupt();
}
}
Runnable
需要并发执行的类,可以通过实现Runnable接口,作为Thread的Target来创建线程对象。
public class TestRunnable implements Runnable{ @Override
public void run() {
while(true) {
System.out.println("thread running...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static void main(String[] args) {
//传入TestRunnable对象作为Target, 开启线程
Thread t = new Thread(new TestRunnable());
t.start();
//采用匿名内部类的方式创建和启动线程
new Thread() {
@Override
public void run() {
System.out.println("Thread的匿名内部类");
}
}.start();
//父类采用匿名实现Runnable接口, 并由子类继承
new Thread(new Runnable() { @Override
public void run() {
System.out.println("父类的线程");
}
}) {
@Override
public void run() {
System.out.println("子类的线程");
}
}.start();
}
}
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实现原理
public class TestCallable implements Callable<Integer>{
//实现Callable并重写call方法作为线程执行体, 并设置返回值1
@Override
public Integer call() throws Exception {
System.out.println("Thread is running...");
Thread.sleep(3000);
return 1;
} public static void main(String[] args) throws InterruptedException, ExecutionException {
//创建Callable实现类的对象
TestCallable tc = new TestCallable();
//创建FutureTask类的对象
FutureTask<Integer> task = new FutureTask<>(tc);
//把FutureTask实现类对象作为target,通过Thread类对象启动线程
new Thread(task).start();
System.out.println("do something else...");
//通过get方法获取返回值
Integer integer = task.get();
System.out.println("The thread running result is :" + integer);
}
}
总结一下,虽然继承Thread类的开发方式相对简单,但因为Java单继承的限制,一般建议通过实现Runnable或Callable接口来创建并启动多线程。
Java并发编程之线程创建和启动(Thread、Runnable、Callable和Future)的更多相关文章
- Java并发编程:如何创建线程?
Java并发编程:如何创建线程? 在前面一篇文章中已经讲述了在进程和线程的由来,今天就来讲一下在Java中如何创建线程,让线程去执行一个子任务.下面先讲述一下Java中的应用程序和进程相关的概念知识, ...
- 【转】Java并发编程:如何创建线程?
一.Java中关于应用程序和进程相关的概念 在Java中,一个应用程序对应着一个JVM实例(也有地方称为JVM进程),一般来说名字默认是java.exe或者javaw.exe(windows下可以通过 ...
- 2、Java并发编程:如何创建线程
Java并发编程:如何创建线程? 在前面一篇文章中已经讲述了在进程和线程的由来,今天就来讲一下在Java中如何创建线程,让线程去执行一个子任务.下面先讲述一下Java中的应用程序和进程相关的概念知识, ...
- Java并发编程:线程的创建
Java并发编程:线程的创建 */--> code {color: #FF0000} pre.src {background-color: #002b36; color: #839496;} J ...
- Java并发编程:线程池的使用
Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...
- Java并发编程:线程池的使用(转)
Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...
- Java并发编程:线程池的使用(转载)
转载自:https://www.cnblogs.com/dolphin0520/p/3932921.html Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实 ...
- Java并发编程:线程池的使用(转载)
文章出处:http://www.cnblogs.com/dolphin0520/p/3932921.html Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实 ...
- [转]Java并发编程:线程池的使用
Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...
随机推荐
- cogs1885 [WC2006]水管局长数据加强版
BZOJ卡不过灰常蛋疼(毕竟人蠢自带巨大常数 这和动态维护最小生成树很像,但加边变成了删边,似乎没法做了. 然后根据之前的套路离线做,删边变成加边,就可以做了orz 二分查找的:(慢 // It is ...
- 网络设备重的loopback接口
回环接口在我们做试验的过程有典型的应用,几乎可以离不开它,一个虚拟的接口,给我带来了很大的方便,有了回环接口,你可以不用为你的PC,来添加第二块物理网卡,就可以完成VM,服务器搭建,群集,VPN等试验 ...
- UWP 自然灾害App在刷新数据后卡死的解决方案
一直以为都在纳闷,为啥我的其他app崩溃次数几乎为0,而单单这个App的崩溃次数简直逆天了,我都不敢相信. 每天都有至少上千次crash...我也是服的 不甘心,趁着这次重构的机会,把代码好好捋了1下 ...
- SSD固态硬盘的GC与Trim
操作系统:其实并没有删除数据: 事实上,它只是在硬盘前的索引区里标记这块文件占用的区域为无效的, 所以等该区域被擦除后,下次数据将要再次写入的时候,可以写入这块被标记的区域. 这也就是为啥那 些所谓的 ...
- 3.PO如何给开发团队讲好故事
敏捷开发系列文章目录 讲出符合开发团队味口的故事. 上一章说了敏捷开发团队的构成与迭代过程,本章重点说一下迭代第一天的计划会议.熟话说“好的开始就成功了一半”,一个迭代的计划会议做得好不好确实直接注定 ...
- OpenGL学习笔记(2) 画一个正方形
画一个正方形 其实,画正方形就是画两个三角形,用四个顶点以及使用索引来实现 完整代码在Square项目的Application.cpp里 先贴上窗口初始化代码 void BaseInit() { gl ...
- jar包冲突常用的解决方法
jar包冲突常见的异常为找不到类(java.lang.ClassNotFoundException).找不到具体方法(java.lang.NoSuchMethodError).字段错误( java.l ...
- leetcode刷题笔记191 位1的个数
题目描述: 编写一个函数,输入是一个无符号整数,返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量). 示例: 输入: 输出: 解释: 32位整数 的二进制表示为 . 题目分析: 判断3 ...
- CHAPTER 24 History of Our Planet 第24章 我们行星的历史
CHAPTER 24 History of Our Planet 第24章 我们行星的历史 Uncovering the bones of ancient beasts is only part of ...
- Python遗传算法工具箱DEAP框架分析
本文主要介绍python遗传算法工具箱DEAP的实现.先介绍deap的如何使用,再深入介绍deap的框架实现,以及遗传算法的各种实现算法. 代码可以参考 https://github.com/suma ...