这一系列的文章暂不涉及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)的更多相关文章

  1. Java并发编程:如何创建线程?

    Java并发编程:如何创建线程? 在前面一篇文章中已经讲述了在进程和线程的由来,今天就来讲一下在Java中如何创建线程,让线程去执行一个子任务.下面先讲述一下Java中的应用程序和进程相关的概念知识, ...

  2. 【转】Java并发编程:如何创建线程?

    一.Java中关于应用程序和进程相关的概念 在Java中,一个应用程序对应着一个JVM实例(也有地方称为JVM进程),一般来说名字默认是java.exe或者javaw.exe(windows下可以通过 ...

  3. 2、Java并发编程:如何创建线程

    Java并发编程:如何创建线程? 在前面一篇文章中已经讲述了在进程和线程的由来,今天就来讲一下在Java中如何创建线程,让线程去执行一个子任务.下面先讲述一下Java中的应用程序和进程相关的概念知识, ...

  4. Java并发编程:线程的创建

    Java并发编程:线程的创建 */--> code {color: #FF0000} pre.src {background-color: #002b36; color: #839496;} J ...

  5. Java并发编程:线程池的使用

    Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...

  6. Java并发编程:线程池的使用(转)

    Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...

  7. Java并发编程:线程池的使用(转载)

    转载自:https://www.cnblogs.com/dolphin0520/p/3932921.html Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实 ...

  8. Java并发编程:线程池的使用(转载)

    文章出处:http://www.cnblogs.com/dolphin0520/p/3932921.html Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实 ...

  9. [转]Java并发编程:线程池的使用

    Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...

随机推荐

  1. python之打包、发布模块

    一.python中针对于写好的模块,并且比人也可以使用改模块,这样就可以以同意的打出来,让别人安装或者赋值过后可以更好的使用以及集成. 二.最近在学习python所以这里主要是记录一下python的打 ...

  2. java多线程系列(一)---多线程技能

    java多线程技能 前言:本系列将从零开始讲解java多线程相关的技术,内容参考于<java多线程核心技术>与<java并发编程实战>等相关资料,希望站在巨人的肩膀上,再通过我 ...

  3. 第四节:Windows系统安装时BIOS设置及注意

    BIOS系统 BIOS是英文"Basic Input Output System"的缩略词,直译过来后中文名称就是"基本输入输出系统".在IBM PC兼容系统上 ...

  4. NO--14 微信小程序,左右联动二

    上一篇讲解了左=>右联动,那个还比较简单,本篇写剩下比较核心的部分,也是本次开发过程中遇到最难的部分,右=>左联动,先简单看一下演示   右左联动.gif 一.关键技术: (1) 小程序 ...

  5. Netty源码分析第5章(ByteBuf)---->第10节: SocketChannel读取数据过程

    Netty源码分析第五章: ByteBuf 第十节: SocketChannel读取数据过程 我们第三章分析过客户端接入的流程, 这一小节带大家剖析客户端发送数据, Server读取数据的流程: 首先 ...

  6. docker 从本地拷贝文件

    1.找到docker的ID全称 docker inspect -f '{{.Id}}' docker_name 2.执行拷贝命令 docker cp 本地文件路径 ID全称:docker路径 3.如果 ...

  7. 高可用OpenStack(Queen版)集群-5.Glance集群

    参考文档: Install-guide:https://docs.openstack.org/install-guide/ OpenStack High Availability Guide:http ...

  8. FICO(费埃哲)评分系统有什么优缺点?在国内的发展怎么样?

    权威回答: FICO的优点很明显: 在美国数据库较全面.一般存储有最近7-10年的个人信用记录,包括银行信用.商业信用甚至保险等. 客观性.计算机自动完成评估工作,克服人为操作的失误. 快捷性.出结果 ...

  9. groupadd命令详解

    基础命令学习目录首页 原文链接:https://wtj6891.iteye.com/blog/2096076 groupadd创建组群 使用groupadd命令可以在系统中创建组群账户 语法: gro ...

  10. SpringBoot初始教程之Redis集中式Session管理

    1.介绍 有关Session的管理方式这里就不再进行讨论,目前无非就是三种单机Session(基于单机内存,无法部署多台机器).基于Cookie(安全性差).基于全局的统一Session管理(redi ...