0036 Java学习笔记-多线程-创建线程的三种方式
创建线程
- 创建线程的三种方式:
- 继承java.lang.Thread
- 实现java.lang.Runnable接口
- 实现java.util.concurrent.Callable接口
- 所有的线程对象都是Thead及其子类的实例
- 每个线程完成一定的任务,其实就是一段顺序执行的代码
继承java.lang.Thread创建线程
package testpack;
public class Test1 {
public static void main(String[] args){
System.out.println("现在是主线程: "+Thread.currentThread()); //用静态方法获取当前正在执行的线程
System.out.println("下面新建两个线程");
new A(100).start();
new A(100).start(); //这里开启两个线程,但不是完成同一件任务
}
}
class A extends Thread{ //继承Thread类
private int tickets;
A (int tick){
tickets=tick;
}
public void run(){
for (;tickets>0;tickets--) {
System.out.println("当前线程:"+getName()+" 卖出第 "+tickets+" 张票。"); //用this获得当前线程
if (tickets==1){
System.out.println("票已卖完,当前线程是: "+getName());
}
}
}
}
- 继承Thread类,开启线程的步骤:
- 定义一个类,继承Thread,重写run()方法,run()方法就是线程要完成的任务
- 创建该类的实例,并调用start()方法启动线程
- 继承Thread类开启的线程,各自完成各自的任务
实现java.lang.Runnable接口创建线程
package testpack;
public class Test1 {
public static void main(String[] args){
System.out.println("现在是主线程: "+Thread.currentThread());
System.out.println("下面新建两个线程");
A a=new A(100); //创建一个任务的对象
new Thread(a,"线程A").start(); //以同一个任务对象为target,开启两个线程
new Thread(a,"线程B").start(); //两个线程完成同一项任务,但是二者协作沟通不好
}
}
class A implements Runnable{ //实现Runnable接口
private int tickets;
A (int tick){
tickets=tick;
}
public void run(){ //同样重写run()方法,就是线程要完成的任务
for (;tickets>0;tickets--) {
System.out.println("当前线程:"+Thread.currentThread()+" 卖出第 "+tickets+" 张票。");
if (tickets==1){
System.out.println("票已卖完,当前线程是: "+Thread.currentThread());
}
}
}
}
- 实现Runnable接口开启线程的步骤:
- 定义一个类,实现Runnbale接口,重写run()方法
- 创建该类的对象,即任务对象
- 以任务对象为target,创建线程,调用start()方法
- 通过Runnable接口创建的多个线程,可以写作完成同一件任务,只是协作方面有待改进
- 另外,自1.8开始,Runnable用了@FunctionalInterface修饰,可以用Lambda表达式创建Runnable对象了
使用Callable和Future创建线程
package testpack;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class Test1 {
public static void main(String[] args){
Callable a=new A(100); //Callble对象不能直接用于Thread的target
FutureTask task=new FutureTask(a); //FutureTask实现了Future接口和Runnable接口,可以用作target
new Thread(task,"线程A").start();
new Thread(task,"线程B").start(); //奇怪的是,该线程未运行,学到后面再看,估计用法不对。
try{
System.out.println(task.get()); //get()方法获取线程任务的返回值,无参数则阻塞到线程执行完成
}catch(Exception ex){
ex.printStackTrace();
}
}
}
class A implements Callable<String>{ //实现Callable接口,注意泛型
private int tickets;
A (int tick){
tickets=tick;
}
public String call(){ //重写call()方法,相当于Runnable的run()方法,但有返回值
for (;tickets>0;tickets--) {
System.out.println("当前线程:"+Thread.currentThread()+" 卖出第 "+tickets+" 张票。");
}
if (tickets==0) {
return "所有票都已卖出!";
}else{
return "票未全部卖出,有异常";
}
}
}
- Callable类似于Runnable,只是前者有返回值、可以抛出异常,后者则不可以;前者不能直接用于target,得用先用FutureTask包装,后者可以直接做target;
- FutureTask实现了Future和Runnable接口,Future接口包含的一些方法:
- V get():获取call方法的返回值,该方法会阻塞,一直等到线程执行结束返回值
- V get(long timeout,TimeUnit unit):获取call()的返回值,但只等待timeout和unit的时间,到时未有返回值,则抛出TimeoutException
- boolean isDone():线程任务是否执行结束
- boolean isCanclled():在任务完成前取消,则返回true
- boolealn cancel(boolean mayInterruptIfRunning):取消Callable任务
- Callable有泛型;也是个函数式接口,可以用Lambda表达式直接创建Callable对象
- 用Callable创建线程任务的步骤:
- 定义一个类,实现Callable接口,重写call()方法。Java8开始,可以用Lambda表达式直接创建对象
- 创建该类的对象,再用FutureTask包装
- 以FutureTask为target创建线程,并start
- 调用FutrueTask的get()方法获取返回值
Thread、Runnable、Callable的比较
- Thread:
- 不能继承其他类;
- 用this获取当前线程
- 多个线程不能执行同一个target任务
- Runnable:
- 可以继承其他类;
- 用Thread.currentThread()获取当前线程
- 多个线程可以执行同一个target任务
- Callable:
- 可以继承其他类;
- 用Thread.currentThread()获取当前线程
- 多个线程可以执行同一个target任务
线程的生命周期
- 新建(New):
- 创建线程对象后,就处于“新建”状态,此时跟一个普通的对象无异
- 只能对新建状态的线程对象,调用start()方法
- 就绪(Runnable):
- 调用start()方法后,处于就绪状态,虚拟机为其创建方法调用栈和程序计数器
- 何时开始运行,取决于JVM的调度
- 就绪状态可能从“新建”、“阻塞”、“运行”三种状态变化而来
- 运行(Running):
- 线程获得了在CPU上运行的权利,开始运行
- 何时中断执行,也取决于调度,一个线程不可能一直占着cpu
- 一个线程,可以调用sleep()或者yield()方法,来主动放弃执行权利
- 阻塞(Blocked):
- 调用sleep()方法
- 调用了一个阻塞式的IO方法,该方法返回之前,被阻塞
- 试图获得一个同步监视器,但该监视器被其他线程所持有
- 等待其他线程的notify()通知
- 调用suspend()方法将该线程挂起。尽量避免该种方法,可能导致死锁
- 死亡(dead):
- 线程任务执行完成
- 线程抛出未捕获的异常或错误
- 调用线程的stop()方法
- 主线程运行结束,不影响子线程的继续运行
- boolean isAlive()方法返回线程是否已死亡:处于新建和死亡两种状态返回false,其他三种状态返回true
- 见下面的线程状态转换图:来源于《疯狂Java讲义 第三版》

start()与run()方法
- 永远不要调用线程对象的run()方法。直接调用run()方法的话,那么就只是一个普通方法,而不是线程任务
- 调用了run()方法之后,会改变线程对象的状态,不再是新建状态,因而也就不能调用start()方法
- 只能对处于新建状态的线程对象调用start()方法,且只能调用一次,否则抛出IllegalThreadStateException异常
0036 Java学习笔记-多线程-创建线程的三种方式的更多相关文章
- Java学习笔记-多线程-创建线程的方式
创建线程 创建线程的方式: 继承java.lang.Thread 实现java.lang.Runnable接口 所有的线程对象都是Thead及其子类的实例 每个线程完成一定的任务,其实就是一段顺序执行 ...
- Java并发编程:Java创建线程的三种方式
目录 引言 创建线程的三种方式 一.继承Thread类 二.实现Runnable接口 三.使用Callable和Future创建线程 三种方式的对比 引言 在日常开发工作中,多线程开发可以说是必备技能 ...
- java创建线程的三种方式及其对比
第一种方法:继承Thread类,重写run()方法,run()方法代表线程要执行的任务.第二种方法:实现Runnable接口,重写run()方法,run()方法代表线程要执行的任务.第三种方法:实现c ...
- AJPFX总结java创建线程的三种方式及其对比
Java中创建线程主要有三种方式: 一.继承Thread类创建线程类 (1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务.因此把run()方法称为执行 ...
- java创建线程的三种方式及其对照
Java中创建线程主要有三种方式: 一.继承Thread类创建线程类 (1)定义Thread类的子类.并重写该类的run方法,该run方法的方法体就代表了线程要完毕的任务.因此把run()方法称为运行 ...
- Java 多线程 创建线程的4种方式
1 继承Thread类,重写run方法.Thread类实现了Runnable接口. 2 实现Runnable接口,重写run方法.相比于继承Thread类,可以避免单继承的缺陷和实现资源共享. 举例: ...
- Java中创建线程的三种方式以及区别
在java中如果要创建线程的话,一般有3种方法: 继承Thread类: 实现Runnable接口: 使用Callable和Future创建线程. 1. 继承Thread类 继承Thread类的话,必须 ...
- Java多线程——创建线程的两种方式
创建线程方式一:继承Thread类. 步骤:1,定义一个类继承Thread类.2,覆盖Thread类中的run方法.3,直接创建Thread的子类对象创建线程.4,调用start方法开启线程并调用线程 ...
- Java多线程-----创建线程的几种方式
1.继承Thread类创建线程 定义Thread类的子类,并重写该类的run()方法,该方法的方法体就是线程需要完成的任务,run()方法也称为线程执行体 创建Thread子类的实例,也就是创建 ...
随机推荐
- WCF学习之旅—WCF服务部署到应用程序(十)
上接 WCF学习之旅—WCF寄宿前的准备(八) WCF学习之旅—WCF服务部署到IIS7.5(九) 五.控制台应用程序宿主 (1) 在解决方案下新建控制台输出项目 ConsoleHosting.如下 ...
- Nginx配置详解
序言 Nginx是lgor Sysoev为俄罗斯访问量第二的rambler.ru站点设计开发的.从2004年发布至今,凭借开源的力量,已经接近成熟与完善. Nginx功能丰富,可作为HTTP服务器,也 ...
- EF Code First Migrations数据库迁移
1.EF Code First创建数据库 新建控制台应用程序Portal,通过程序包管理器控制台添加EntityFramework. 在程序包管理器控制台中执行以下语句,安装EntityFramewo ...
- 从零开始编写自己的C#框架(22)——添加普通列表页面
普通列表页面指的是上一章那种有层次感列表以外的正常列表页面,由于上一章已讲解了正常添加页面的相关操作了,所以部分相关的操作本章节就不再罗嗦重复一次了.大家可以试试先用本章内容中的一些简单介绍,自己使用 ...
- JavaScript学习总结(二)——闭包、IIFE、apply、函数与对象
一.闭包(Closure) 1.1.闭包相关的问题 请在页面中放10个div,每个div中放入字母a-j,当点击每一个div时显示索引号,如第1个div显示0,第10个显示9:方法:找到所有的div, ...
- ASP.NET Core 中文文档 第三章 原理(1)应用程序启动
原文:Application Startup 作者:Steve Smith 翻译:刘怡(AlexLEWIS) 校对:谢炀(kiler398).许登洋(Seay) ASP.NET Core 为你的应用程 ...
- 单链表的C++实现(采用模板类)
采用模板类实现的好处是,不用拘泥于特定的数据类型.就像活字印刷术,制定好模板,就可以批量印刷,比手抄要强多少倍! 此处不具体介绍泛型编程,还是着重叙述链表的定义和相关操作. 链表结构定义 定义单链表 ...
- 学习javascript数据结构(一)——栈和队列
前言 只要你不计较得失,人生还有什么不能想法子克服的. 原文地址:学习javascript数据结构(一)--栈和队列 博主博客地址:Damonare的个人博客 几乎所有的编程语言都原生支持数组类型,因 ...
- C#开发微信门户及应用(28)--微信“摇一摇·周边”功能的使用和接口的实现
”摇一摇周边“是微信提供的一种新的基于位置的连接方式.用户通过“摇一摇”的“周边”页卡,可以与线下商户进行互动,获得商户提供的个性化的服务.微信4月份有一个赠送摇一摇设备的活动,我们有幸获得赠送资格, ...
- android权限
一.WebView 访问internet 的权限: 1.在layout中增加一个WebView 控件: <WebView android:layout_width="match_par ...