文档章节

关于线程的创建

M
 MarinJ_Shao
发布于 06/24 22:36
字数 1825
阅读 5
收藏 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
博文 11
码字总数 4616
作品 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
JAVA多线程和并发基础面试问答

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

清风傲剑
2014/12/06
0
0

没有更多内容

加载失败,请刷新页面

加载更多

如何通过 J2Cache 实现分布式 session 存储

做 Java Web 开发的人多数都会需要使用到 session (会话),我们使用 session 来保存一些需要在两个不同的请求之间共享数据。一般 Java 的 Web 容器像 Tomcat、Resin、Jetty 等等,它们会在...

红薯
今天
3
0
C++ std::thread

C++11提供了std::thread类来表示一个多线程对象。 1,首先介绍一下std::this_thread命名空间: (1)std::this_thread::get_id():返回当前线程id (2)std::this_thread::yield():用户接口...

yepanl
今天
3
0
Nignx缓存文件与动态文件自动均衡的配置

下面这段nginx的配置脚本的作用是,自动判断是否存在缓存文件,如果有优先输出缓存文件,不经过php,如果没有,则回到php去处理,同时生成缓存文件。 PHP框架是ThinkPHP,最后一个rewrite有关...

swingcoder
今天
2
0
20180920 usermod命令与用户密码管理

命令 usermod usermod 命令的选项和 useradd 差不多。 一个用户可以属于多个组,但是gid只有一个;除了gid,其他的组(groups)叫做扩展组。 usermod -u 1010 username # 更改用户idusermod ...

野雪球
今天
3
0
Java网络编程基础

1. 简单了解网络通信协议TCP/IP网络模型相关名词 应用层(HTTP,FTP,DNS等) 传输层(TCP,UDP) 网络层(IP,ICMP等) 链路层(驱动程序,接口等) 链路层:用于定义物理传输通道,通常是对...

江左煤郎
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部