创建线程

  • 创建线程的三种方式:

    • 继承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学习笔记-多线程-创建线程的三种方式的更多相关文章

  1. Java学习笔记-多线程-创建线程的方式

    创建线程 创建线程的方式: 继承java.lang.Thread 实现java.lang.Runnable接口 所有的线程对象都是Thead及其子类的实例 每个线程完成一定的任务,其实就是一段顺序执行 ...

  2. Java并发编程:Java创建线程的三种方式

    目录 引言 创建线程的三种方式 一.继承Thread类 二.实现Runnable接口 三.使用Callable和Future创建线程 三种方式的对比 引言 在日常开发工作中,多线程开发可以说是必备技能 ...

  3. java创建线程的三种方式及其对比

    第一种方法:继承Thread类,重写run()方法,run()方法代表线程要执行的任务.第二种方法:实现Runnable接口,重写run()方法,run()方法代表线程要执行的任务.第三种方法:实现c ...

  4. AJPFX总结java创建线程的三种方式及其对比

    Java中创建线程主要有三种方式: 一.继承Thread类创建线程类 (1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务.因此把run()方法称为执行 ...

  5. java创建线程的三种方式及其对照

    Java中创建线程主要有三种方式: 一.继承Thread类创建线程类 (1)定义Thread类的子类.并重写该类的run方法,该run方法的方法体就代表了线程要完毕的任务.因此把run()方法称为运行 ...

  6. Java 多线程 创建线程的4种方式

    1 继承Thread类,重写run方法.Thread类实现了Runnable接口. 2 实现Runnable接口,重写run方法.相比于继承Thread类,可以避免单继承的缺陷和实现资源共享. 举例: ...

  7. Java中创建线程的三种方式以及区别

    在java中如果要创建线程的话,一般有3种方法: 继承Thread类: 实现Runnable接口: 使用Callable和Future创建线程. 1. 继承Thread类 继承Thread类的话,必须 ...

  8. Java多线程——创建线程的两种方式

    创建线程方式一:继承Thread类. 步骤:1,定义一个类继承Thread类.2,覆盖Thread类中的run方法.3,直接创建Thread的子类对象创建线程.4,调用start方法开启线程并调用线程 ...

  9. Java多线程-----创建线程的几种方式

       1.继承Thread类创建线程 定义Thread类的子类,并重写该类的run()方法,该方法的方法体就是线程需要完成的任务,run()方法也称为线程执行体 创建Thread子类的实例,也就是创建 ...

随机推荐

  1. Nginx 服务器 之Nginx与tomcat实现负载均衡

      本文讲解我们如何使用Nginx做反向带服务器,实现nginx与tomcat服务器集群做负载均衡. 一.nginx与tomcat实现负载均衡 1.在/usr/local/ngnix/conf  创建 ...

  2. 【WCF】自定义地址头的筛选器

    前面的文章中,老周已向大伙伴们介绍了如何在终结点上使用地址头,只要服务是沿着该终结点调用的,那么每一次调用都会自动把地址头插入到SOAP消息的Header列表中. 而通过前一篇文章中的示例,大家也看到 ...

  3. 高性能JavaScript--快速响应的用户界面(简要学习笔记三)

    1.浏览器线程:用于执行JavaScript和更新用户界面的进程被称为“浏览器UI线程”.   2. <1>定时器的出现让出UI线程控制权 setTimeout(),setInterval ...

  4. 前端学HTTP之摘要认证

    前面的话 上一篇介绍的基本认证便捷灵活,但极不安全.用户名和密码都是以明文形式传送的,也没有采取任何措施防止对报文的篡改.安全使用基本认证的唯一方式就是将其与SSL配合使用 摘要认证与基本认证兼容,但 ...

  5. 记一次使用 android 自带 WebView 做富文本编辑器之API、机型的兼容及各种奇葩bug的解决

    转载请声明出处(http://www.cnblogs.com/linguanh/) 目录 1,测试设备介绍 2,开源项目richeditor及CrossWalk的选择 3,遇到的bug及其解决方法 4 ...

  6. HTTP Method详细解读(`GET` `HEAD` `POST` `OPTIONS` `PUT` `DELETE` `TRACE` `CONNECT`)

    前言 HTTP Method的历史: HTTP 0.9 这个版本只有GET方法 HTTP 1.0 这个版本有GET HEAD POST这三个方法 HTTP 1.1 这个版本是当前版本,包含GET HE ...

  7. VMware-存储断网之后无法添加vmx到清单

    由于发生了单点故障,笔者最近处理了一个case,其中一些经验非常希望和大家分享. 问题原因: Technorati 标签: VMware,虚拟机,vmx,锁定,干货 某环境使用VMware的ESXi5 ...

  8. 使用java传参调用exe并且获取程序进度和返回结果的一种方法

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.背景 在某个项目中需要考虑使用java后台调用由C#编写的切图程序( ...

  9. 压力测试之badboy和Jmeter的简单使用方法

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.背景 所谓压力测试是指,通过确定一个系统的瓶颈或者不能接收的性能点, ...

  10. C#泛型方法解析

    C#2.0引入了泛型这个特性,由于泛型的引入,在一定程度上极大的增强了C#的生命力,可以完成C#1.0时需要编写复杂代码才可以完成的一些功能.但是作为开发者,对于泛型可谓是又爱又恨,爱的是其强大的功能 ...