Java并发编程(二)创建线程的三种方法
进程与线程
1. 进程
进程和代码之间的关系就像音乐和乐谱之间的关系一样,演奏结束的时候音乐就不存在了但乐谱还在;程序执行结束的时候进程就消失了但代码还在,而计算机就是代码的演奏家。
2. 线程
线程可以比喻成演奏过程中的某一种乐器的声音,乐器声音的种类可以很少,但是不能一个都没有——一个进程至少包含一个线程。线程是程序执行的核心,就像没有了具体乐器的声音就没有了音乐一样。
创建线程的三种方式
1. 继承Thread方法
1). 定义线程类
继承Thread方法时,需要重写Thread的run()方法。run()方法中的代码就是你要并发执行的代码。
class ExampleThread extends Thread {
@Override
public void run() {
doSomething();
}
}
2). 执行线程类的代码
在main()方法里创建一个ExampleThread对象,调用该对象的start()方法,start()方法会通过对系统底层的一系列操作,创建出一个相应的线程,与当前线程并发执行。如果直接调用run()方法,程序将执行完run()方法后才会执行main()方法中后面的代码,这样就是单线程执行而不是多线程并发执行了。
public class CreateThread {
public static void main(String[] args) {
ExampleThread thread = new ExampleThread();
thread.start();//注意是start(),不是run()
//下面的代码可以和run()方法里的代码并发执行
doSomethingElse();
}
}
2. 实现Runnable接口
1). 定义线程类
使用Runnable接口与继承Thread类的用法类似,也是在新的类中编写run()方法,方法中的内容就是新建线程要执行的代码。
class ExampleThread implements Runnable {
@Override
public void run() {
doSomething();
}
}
2). 执行线程类的代码
实现Runnable接口的类中没有start()方法,因此我们应该为这个线程“找一个”start()方法。Thread类有一个带Runnable参数的构造方法,我们将ExampleThread的对象作为参数传到Thread的构造方法中,这时候再调用Thread对象的start()方法就可以生成一个线程了,是不是有种“借鸡生蛋”的感觉。
public class CreateThread {
public static void main(String[] args) {
Runnable runnable = new ExampleThread()
Thread thread = new Thread(runnable);
thread.start();//注意是start(),不是run()
//下面的代码可以和run()方法里的代码并发执行
doSomethingElse();
}
}
3. 实现Callable接口
1). 定义线程类
前面说的使用Thread和Runnable实现多线程的方法都不能从上一个线程中得到返回值,可是有些时候当一个线程运行结束的时候你想让它返回一些有价值的信息,比如某些操作是否执行成功了。Callable接口就是用于解决这类问题的,Callable接口是一个带泛型的接口,泛型的类型就是线程返回值的类型。实现Callable接口中的call()方法,方法的返回类型与泛型的类型相同。
class ExampleThread implements Callable<String> {
@Override
public String call() {
doSomething();
return "Some Value";
}
}
2). 执行线程类的代码
执行这个线程类的方法与Runnable类似,但是Callable在调用start()方法之前需要先包一层FutureTask,FutureTask用于获得线程返回的数值,具体代码如下:
public class CreateThread {
public static void main(String[] args) {
Callable<String> callable = new ExampleThread();
FutureTask<String> task = new FutureTask<String>(callable);
Thread thread = new Thread(task);
thread.start();//注意是start(),不是call()
doSomethingElse();
try {
String returnVal = task.get();//这里就得到了线程的返回值
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
这里有一个需要注意的地方,当调用task.get()的时候,新创建的线程可能还没有执行完,这时调用get()方法当前线程就会被阻塞,直到新创建的线程执行结束。因此,task.get()代码最好是在当前线程不得不用这个返回值时再调用,这样当前线程可以继续执行与返回值无关的代码。
总结
通过继承Thread类和实现Runnable接口生成的线程都是没有返回值的,实现Callable接口的线程可以有返回值,但是要注意不要过早的阻塞当前线程。
继承Thread类和实现Runnable接口二者之间各有好处,继承Thread类可以方便的使用到start()方法启动线程,但同时也带来一个弊端:继承使新类和Thread类产生强耦合,此外,由于Java不支持多继承,新的类也不能再继承其他的类。实现Runnable接口与之相反。
那么问题来了,Thread和Runnable该如何选择呢?我的建议是尽量使用Runnable,有的人可能会说:用Runnable还是要调用Thread的start()方法,还不如直接用Thread呢。嘿嘿,其实线程池可以代替Thread的start(),预知详情,且看下文分解。公众号:今日说码。关注我的公众号,可查看连载文章。遇到不理解的问题,直接在公众号留言即可。
Java并发编程(二)创建线程的三种方法的更多相关文章
- JAVA并发编程学习笔记------线程的三种创建方式
创建线程一般有如下几个方式: 1. 通过继承Thread类来创建一个线程: /** * 步骤1:定义一个继承Thread类的子类 * 步骤2:构造子类的一个对象 * 步骤3:启动线程: * */ pu ...
- 《Java多线程面试题》系列-创建线程的三种方法及其区别
1. 创建线程的三种方法及其区别 1.1 继承Thread类 首先,定义Thread类的子类并重写run()方法: package com.zwwhnly.springbootaction.javab ...
- java创建线程的三种方法
这里不会贴代码,只是将创建线程的三种方法做个笼统的介绍,再根据源码添加上自己的分析. 通过三种方法可以创建java线程: 1.继承Thread类. 2.实现Runnable接口. 3.实现Callab ...
- java多线程编程(二创建线程)
1.概念 因为java是完全面向对象的,所以在java中,我们说的线程,就是Thread类的一个实例对象.所以,一个线程就是一个对象,它有自己字段和方法. 2.创建线程 创建线程有 ...
- 0036 Java学习笔记-多线程-创建线程的三种方式
创建线程 创建线程的三种方式: 继承java.lang.Thread 实现java.lang.Runnable接口 实现java.util.concurrent.Callable接口 所有的线程对象都 ...
- python 多线程编程之threading模块(Thread类)创建线程的三种方法
摘录 python核心编程 上节介绍的thread模块,是不支持守护线程的.当主线程退出的时候,所有的子线程都将终止,不管他们是否仍在工作. 本节开始,我们开始介绍python的另外多线程模块thre ...
- JAVA中创建线程的三种方法及比较
JAVA中创建线程的方式有三种,各有优缺点,具体如下: 一.继承Thread类来创建线程 1.创建一个任务类,继承Thread线程类,因为Thread类已经实现了Runnable接口,然后重写run( ...
- Java基础_线程的使用及创建线程的三种方法
线程:线程是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务. 进程:进 ...
- Java中创建线程的三种方法以及区别
Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例.Java可以用三种方式来创建线程,如下所示: 1)继承Thread类创建线程 2)实现Runnable接口创建线 ...
随机推荐
- vmware 虚拟机导入OVF出现路径错误
现状: 需要将原有数据中心的VM虚拟机导出本地OVF模板,然后迁移至新的机房虚拟化环境后从新导入. 问题: 导入OVF时候,先出现错误问题1,修复完成后,出现错误问题2 1. OVF迁移至本地以后,导 ...
- 006Spring面向切面
01.基本术语---->POM中配置spring-aspects 1.通知(Advice)---->要做的事 前置通知(@Before) 后置通知(@After) 返回通知(@AfterR ...
- LintCode2016年算法比赛----二叉树的所有路径
二叉树的所有路径 题目描述 给定一棵二叉树,找从根节点到叶子节点的所有路径 样例 给出下面这课二叉树: 1 / \ 2 3 \ 5 所有根到叶子的路径为: [ "1->2->5& ...
- Leetcode算法比赛---- Lexicographical Numbers
问题描述 Given an integer n, return 1 - n in lexicographical order. For example, given 13, return: [1,10 ...
- Jarvis OJ-Reverse题目Writeup
做一道更一道吧233333 DD-Android Easy 下载apk,先安装一下试试吧…… 猜测是输入正确的内容后给flag吧 将后缀改成zip,解压,用dex2jar处理classes.dex,然 ...
- PRML读书笔记——线性回归模型(上)
本章开始学习第一个有监督学习模型--线性回归模型."线性"在这里的含义仅限定了模型必须是参数的线性函数.而正如我们接下来要看到的,线性回归模型可以是输入变量\(x\)的非线性函数. ...
- JUnit测试模块使用
JUnit简介 JUnit是一个Java语言的单元测试框架.它由Kent Beck和Erich Gamma建立,逐渐成为源于Kent Beck的sUnit的xUnit家族中最为成功的一个. JUn ...
- redis 序列化get、set获取不到原因记录
最近项目里面出现了个bug,把数据从数据库中读取后又存取到redis里面,之后再读取.奇怪的是,有一个 字段读取不到. public class Circle { private String id; ...
- Java BigDecimal初探
更新时间:2016-03-17 一.引言 <Effactive Java>中有这样的描述:float和double类型的主要设计目标是为了科学计算和工程计算.他们执行二进制浮点运算,这是为 ...
- POP动画[2]
POP动画[2] 1:定制控制器间的转场动画. 源码有点多-_-!! // // RootViewController.h // Animation // // Copyright (c) 2014年 ...