文档章节

关于线程的创建

M
 MarinJ_Shao
发布于 06/24 22:36
字数 1825
阅读 1
收藏 0
点赞 0
评论 0

 

转自自己的笔记:

http://note.youdao.com/noteshare?id=87584d4874acdeaf4aa027bdc9cb7324&sub=B49E8956E145476191C3FD1E4AB40DFA

1.创建线程的方法

 

Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。Java可以用三种方式来创建线程,如下所示:

1)继承Thread类创建线程

2)实现Runnable接口创建线程

3)使用Callable和Future创建线程

extendeds Thread(); implements Runnable; implements Callable(有返回值);

------------------------继承Thread类创建线程---------------------

通过继承Thread类来创建并启动多线程的一般步骤如下:

1)定义Thread类的子类,并重写run()方法,该方法的方法体就是线程需要完成的任务,run()方法也叫线程的执行体;

2)创建Thread子类实例,也就是创建线程对象;

3)启动线程,即调用线程的start()方法。

代码实例

public class MyThread extends Thread {

public void run(){

//重写方法

}

}

public class Main{

public static void main(String[] args) {

new MyThread().start();//创建并重启线程

}

}

------------------------实现Runnable接口创建线程---------------------

通过实现Runnable接口创建并启动线程一般步骤如下:

1)定义runnable接口的实现类,重写run()方法,run()方法也是线程的执行体;

2)创建Runnable实现类的实例,并用该实例作为Thread的targer来创建Thread对象,这个Thread对象才是真正的线程对象;

3)通过调用线程的start()方法来启动线程。

代码实例:

public class MyThread2 implements Runnable {//实现Runnable接口

  public void run(){

  //重写run方法

  }

}

public class Main {

  public static void main(String[] args){

    //创建并启动线程

    MyThread2 myThread=new MyThread2();

    Thread thread=new Thread(myThread);

    thread().start();

    //或者    new Thread(new MyThread2()).start();

  }

}

综合上述五种情况分析:

1.如果没有继承Thread类只是创建普通Thread类对象,则线程什么都不做

2.如果继承了Thread类:

    a.如果重写了run()方法:则JVM调用该方法

    b.如果没有重写run()方法:则JVM调用JDK自己定义的run()方法

         I.如果指定了target,则调用target的run()方法

         II.如果没有指定target,则什么都不做。

总结:

对于Thread(Runnable target).start()来说:不管target是否为空,Thread都会首先调用自己的run()方法:如果重写了该方法,且没有super.run(),则永远都不会调用Runnable中实现的run()方法;有super.run()的话,则首先调用完Thread自己的run()方法,然后决定是否再调用target中的run()方法;如果没有重写Thread自己的run()方法,则会判断target是否为空,然后决定是否调用target的run()方法。

 

------------------------使用Callable和Future创建线程---------------------

和Runnable接口不一样,Callable接口提供了一个call()方法作为线程执行体,call()方法比run()方法功能要强大。

》call()方法可以有返回值

》call()方法可以声明抛出异常

Java5提供了Future接口来代表Callable接口里call()方法的返回值,并且为Future接口提供了一个实现类FutureTask,这个实现类既实现了Future接口,还实现了Runnable接口,因此可以作为Thread类的target。在Future接口里定义了几个公共方法来控制它关联的Callable任务。

public interface Future<V> { boolean cancel(boolean mayInterruptIfRunning); boolean isCancelled(); boolean isDone(); V get() throws InterruptedException, ExecutionException; V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }

>cancel(boolean mayInterruptIfRunning):试图取消该Future里面关联的Callable任务

>V get():返回Callable里call()方法的返回值,调用这个方法会导致程序阻塞,必须等到子线程结束后才会得到返回值

>V get(long timeout,TimeUnit unit):返回Callable里call()方法的返回值,最多阻塞timeout时间,经过指定时间没有返回抛出TimeoutException

>isDone():若Callable任务完成,返回true

>isCancelled():如果在Callable任务正常完成前被取消,返回true

因为Future只是一个接口,所以是无法直接用来创建对象使用的,因此就有了下面的FutureTask。

public interface RunnableFuture<V> extends Runnable, Future<V> { void run(); }

public class FutureTask<V> implements RunnableFuture<V> {

//....l略....

public FutureTask(Callable<V> callable) {

if (callable == null)

throw new NullPointerException();

this.callable = callable;

this.state = NEW; // ensure visibility of callable

}

public FutureTask(Runnable runnable, V result) {

this.callable = Executors.callable(runnable, result);

this.state = NEW; // ensure visibility of callable

//....l略....

}

可以看到这个接口实现了Runnable和Future接口,接口中的具体实现由FutureTask来实现。这个类的两个构造方法如下 :

创建并启动有返回值的线程的步骤如下:

1)创建Callable接口的实现类,并实现call()方法,然后创建该实现类的实例(从java8开始可以直接使用Lambda表达式创建Callable对象)。

2)使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值

3)使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口)

4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

public class CallableThread implements Callable<String>{

@Override

public String call() throws Exception {

Thread.sleep(1000);

return "This is callableThread";

}

}

public void test1(){

CallableThread callableThread = new CallableThread();

FutureTask<String> future = new FutureTask<>(callableThread);

new Thread(future).start();

String result="初始化值";

try{

//result = future.get();

result = future.get(1000,TimeUnit.MILLISECONDS);//时间小于等于方法执行时间抛出超时异常

} catch (InterruptedException e) {

e.printStackTrace();

} catch (Exception e){

e.printStackTrace();

}

System.out.println(result);

}

--------------------------------------三种创建线程方法对比--------------------------------------

实现Runnable和实现Callable接口的方式基本相同,不过是后者执行call()方法有返回值,后者线程执行体run()方法无返回值,因此可以把这两种方式归为一种这种方式与继承Thread类的方法之间的差别如下:

1、线程只是实现Runnable或实现Callable接口,还可以继承其他类。

2、这种方式下,多个线程可以共享一个target对象,非常适合多线程处理同一份资源的情形。

3、但是编程稍微复杂,如果需要访问当前线程,必须调用Thread.currentThread()方法。

4、继承Thread类的线程类不能再继承其他父类(Java单继承决定)。

注:一般推荐采用实现接口的方式来创建多线程

一个FutureTask的例子

public class MyCallable implements Callable<String> { private long waitTime; public MyCallable(int timeInMillis){ this.waitTime=timeInMillis; } @Override public String call() throws Exception { Thread.sleep(waitTime); //return the thread name executing this callable task return Thread.currentThread().getName(); } }

public class FutureTaskExample { public static void main(String[] args) {

// 要执行的任务 MyCallable callable1 = new MyCallable(1000); MyCallable callable2 = new MyCallable(2000);

// 将Callable写的任务封装到一个由执行者调度的FutureTask对象 FutureTask<String> futureTask1 = new FutureTask<String>(callable1); FutureTask<String> futureTask2 = new FutureTask<String>(callable2);

// 创建线程池并返回ExecutorService实例 ExecutorService executor = Executors.newFixedThreadPool(2); executor.execute(futureTask1); // 执行任务 executor.execute(futureTask2); while (true) { try {

// 两个任务都完成 if(futureTask1.isDone() && futureTask2.isDone()){ System.out.println("Done");

// 关闭线程池和服务 executor.shutdown(); return; } if(!futureTask1.isDone()){ // 任务1没有完成,会等待,直到任务完成 System.out.println("FutureTask1 output="+futureTask1.get()); } System.out.println("Waiting for FutureTask2 to complete"); String s = futureTask2.get(200L, TimeUnit.MILLISECONDS); if(s !=null){ System.out.println("FutureTask2 output="+s); } } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); }catch(TimeoutException e){ //do nothing } } } }

输出结果如下:

FutureTask1 output=pool-1-thread-1 Waiting for FutureTask2 to complete Waiting for FutureTask2 to complete Waiting for FutureTask2 to complete Waiting for FutureTask2 to complete Waiting for FutureTask2 to complete FutureTask2 output=pool-1-thread-2 Done

 

© 著作权归作者所有

共有 人打赏支持
M
粉丝 0
博文 8
码字总数 2981
作品 0
杭州
程序员
关于ThreadLocal的理解

ThreadLocal ThreadLocal是一个关于创建线程局部变量的类。 通常情况下,我们创建的变量是可以被任何一个线程访问并修改的。而使用ThreadLocal创建的变量只能被当前线程访问,其他线程则无法...

雨翔河
2016/10/30
39
0
JAVA多线程和并发基础面试问答

原文链接 译文连接 作者:Pankaj 译者:郑旭东 校对:方腾飞 多线程和并发问题是Java技术面试中面试官比较喜欢问的问题之一。在这里,从面试的角度列出了大部分重要的问题,但是你仍然应该牢...

雷神雨石
2014/07/19
0
2
JAVA多线程和并发基础面试问答

多线程和并发问题是Java技术面试中面试官比较喜欢问的问题之一。在这里,从面试的角度列出了大部分重要的问题,但是你仍然应该牢固的掌握Java多线程基础知识来对应日后碰到的问题。(校对注:...

LCZ777
2014/05/26
0
0
JAVA多线程和并发基础面试问答

Java多线程面试问题 1. 进程和线程之间有什么不同? 一个进程是一个独立(self contained)的运行环境,它可以被看作一个程序或者一个应用。而线程是在进程中执行的一个任务。Java运行环境是一...

hanzhankang
2014/01/20
0
0
Android Handler异步通信:深入详解Handler机制源码

前言 在开发的多线程应用场景中,机制十分常用 今天,我将手把手带你深入分析 机制的源码,希望你们会喜欢 目录 1. Handler 机制简介 在多线程的应用场景中,将工作线程中需更新的操作信息 ...

carson_ho
05/21
0
0
JAVA多线程和并发基础面试问答

Java多线程面试问题 1. 进程和线程之间有什么不同? 一个进程是一个独立(self contained)的运行环境,它可以被看作一个程序或者一个应用。而线程是在进程中执行的一个任务。Java运行环境是一...

清风傲剑
2014/12/06
0
0
java多线程之:创建开启一个线程的开销

---->关于时间,创建线程使用是直接向系统申请资源的,这里调用系统函数进行分配资源的话耗时不好说。 ---->关于资源,Java线程的线程栈所占用的内存是在Java堆外的,所以是不受java程序控制的...

无信不立
2016/08/11
0
0
Java多线程和并发基础面试题

本文帮助大家掌握Java多线程基础知识来对应日后碰到的问题,具体内容如下 一、Java多线程面试问题 1. 进程和线程之间有什么不同? 一个进程是一个独立(self contained)的运行环境,它可以被看...

火力全開
2016/10/09
11
0
Android 多线程: 完全解析线程池ThreadPool原理&使用

前言 对于多线程,大家应该很熟悉。但是,大家了解线程池吗? 今天,我将带大家全部学习关于线程池的所有知识。 目录 1. 简介 // 通过 构造方法 配置核心参数Executor executor = new Threa...

carson_ho
04/24
0
0
关于ThreadLocal

一、认识ThreadLocal 1.ThreadLocal概念 为了防止任务在共享资源上产生冲突,我们可以使用同步机制,还可以选择根除对变量的共享来防止冲突。线程本地的存储是一种自动化的机制,可以为使用相...

Key_Stone
2016/09/05
47
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

重写视频播放进度条

需要注意的地方,基于html vedio 标准使用期去了解一下 1.想去掉视频默认的播放条,去掉controls属性。 2.需要预加载视频加上preload="auto"属性。 1.js代码 $(function(){ init(); }); var ...

轻量级赤影
4分钟前
0
0
saltstack管理任务计划-添加&删除

1.服务端配置 >>编辑 top.sls 文件 # vim /srv/salt/top.sls //修改为如下 base: '192.168.*.*': - crontest >>编辑crontest.sls文件添加计划任务 cron-test: cron.present: - name: /bin/to......

硅谷课堂
4分钟前
0
0
sql中多表查询及其左连字段

SELECT s.*,t.teach_name FROM `stu` s, `teacher` t WHERE s.teacher_id = t.row_id AND s.teacher_id = 1 s.* s表中 全部字段 t.teach_name t表中teach_name 字段 SELECT s.*,t.teach_nam......

森火
7分钟前
0
0
ES9-mapping参数

1.概述 ElasticSearch提供了丰富的参数对文档字段进行定义,比如字段的分词器、字段权重、日期格式、检索模型等等。可以查看官网每个参数的定义及使用:https://www.elastic.co/guide/en/ela...

贾峰uk
10分钟前
1
0
Java泛型学习

一、泛型的概念 List list = new ArrayList(); list.add("corn"); String name = (String) list.get(0); 1、这里将一个对象放入集合中,集合不会记住次对象的类型,当再次从集合中取出此对象...

cjxcloud
12分钟前
0
0
android屏幕适配

android屏幕适配 采用的是鸿阳的适配方式,项目依赖: compile 'com.zhy:autolayout:1.4.5' 使用步骤: 在manifest文件中标注你的设计图尺寸 <meta-data android:name="design_width" andro...

android-key
16分钟前
0
0
istio 0.8 安装步骤

============================ istio 0.8 安装步骤--------------------------------------- istio 0.8 安装步骤 1.安装k8s环境 参考:http://sealyun.com/pro/products/ master,salve两台机......

xiaomin0322
18分钟前
2
0
tmux 退出不干净问题

tmux ls 已经没有窗口了,但是显示还有在登入 只有强制T下线了。

NLGBZJ
19分钟前
0
0
卡辛斯基的警告

卡辛斯基的警告 作者: 阮一峰 1、 1978年5月25日,美国西北大学的工程教授巴克利·克利斯(Buckley Crist),收到了邮政局退回的一个包裹。 这个包裹寄往芝加哥大学,但是收件人“查无此人”...

祖冲之
19分钟前
1
0
如何一周内学会编程?实战项目中总结经验[图]

如何一周内学会编程?实战项目中总结经验[图]: 我知道有很多草根肯定都是日常有很多各种各样,自己认为非常有价值的想法,但是,苦于自己没有技术,无法实现自己的需求,典型的属于“就差一...

原创小博客
24分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部