进程与线程

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并发编程(二)创建线程的三种方法的更多相关文章

  1. JAVA并发编程学习笔记------线程的三种创建方式

    创建线程一般有如下几个方式: 1. 通过继承Thread类来创建一个线程: /** * 步骤1:定义一个继承Thread类的子类 * 步骤2:构造子类的一个对象 * 步骤3:启动线程: * */ pu ...

  2. 《Java多线程面试题》系列-创建线程的三种方法及其区别

    1. 创建线程的三种方法及其区别 1.1 继承Thread类 首先,定义Thread类的子类并重写run()方法: package com.zwwhnly.springbootaction.javab ...

  3. java创建线程的三种方法

    这里不会贴代码,只是将创建线程的三种方法做个笼统的介绍,再根据源码添加上自己的分析. 通过三种方法可以创建java线程: 1.继承Thread类. 2.实现Runnable接口. 3.实现Callab ...

  4. java多线程编程(二创建线程)

    1.概念           因为java是完全面向对象的,所以在java中,我们说的线程,就是Thread类的一个实例对象.所以,一个线程就是一个对象,它有自己字段和方法. 2.创建线程 创建线程有 ...

  5. 0036 Java学习笔记-多线程-创建线程的三种方式

    创建线程 创建线程的三种方式: 继承java.lang.Thread 实现java.lang.Runnable接口 实现java.util.concurrent.Callable接口 所有的线程对象都 ...

  6. python 多线程编程之threading模块(Thread类)创建线程的三种方法

    摘录 python核心编程 上节介绍的thread模块,是不支持守护线程的.当主线程退出的时候,所有的子线程都将终止,不管他们是否仍在工作. 本节开始,我们开始介绍python的另外多线程模块thre ...

  7. JAVA中创建线程的三种方法及比较

    JAVA中创建线程的方式有三种,各有优缺点,具体如下: 一.继承Thread类来创建线程 1.创建一个任务类,继承Thread线程类,因为Thread类已经实现了Runnable接口,然后重写run( ...

  8. Java基础_线程的使用及创建线程的三种方法

    线程:线程是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务. 进程:进 ...

  9. Java中创建线程的三种方法以及区别

    Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例.Java可以用三种方式来创建线程,如下所示: 1)继承Thread类创建线程 2)实现Runnable接口创建线 ...

随机推荐

  1. Angular进阶教程一

    6 AngularJS进阶 6.1数据绑定原理研究 Angular用户都想知道数据绑定是怎么实现的.你可能会看到各种各样的词汇:$watch.$apply.$digest.dirty-checking ...

  2. 工具Sequel Pro简介

    从图中可以看到,sequel工作界面主要分成三部分,左边侧边显示的是当前所连接的数据库中存在的数据表,右侧上半部分则是用于写sql的地方,在Mac环境下按command+R键会执行你所写的sql,右侧 ...

  3. java中字节流与字符流以及字节流多余字节问题

    1.字节流 字节流byte为单位对文件的数据进行写入与读取操作.字节的方式在复制音频图片文件时比较适用,但在对于普通文件的读写上有两大缺陷: 第一,字节流在读取中文字符时,若设定的字节数组长度刚好末尾 ...

  4. 我只是个搬运工,walle

    安装 改进本文 walle 瓦力 自动化1.简洁安装指南 git clone git@github.com:meolu/walle-web.gitcd walle-webvi config/web.p ...

  5. PURGE 的用法说明

    PURGE PurposeUse the PURGE statement to remove a table or index from your recycle bin and release al ...

  6. Sqlite EF6注册

    在EF6使用Sqlite的时候.Sqlite需要安装sqlite-netFx40-setup-bundle-x64-2010-1.0.97.0.exe.我不想在项目发布的时候,安装的时候执行该程序,于 ...

  7. 如何提高Ajax性能

    1.适当使用缓存机制 2.使用CDN内容分发来访问Jquery脚本: (1)自己公司架设CDN服务器 (2)使用第三方公司的,比如微软,谷歌等公司的CDN,但有时候不太靠谱 3.JS/CSS文件的打包 ...

  8. Effective C++(7) 为多态基类声明virtual析构函数 or Not

    问题聚焦: 已经对一个对象执行了delete语句,还会发生内存泄漏吗? 先来看个demo: // 计时器类 class TimeKeeper { public: TimeKeeper(); ~Time ...

  9. Java常见错误列表

    Java常见错误列表: 找不到符号(symbol) 类X是public的,应该被声明在名为X.java的文件中 缺失类.接口或枚举类型 缺失X 缺失标识符 非法的表达式开头 类型不兼容 非法的方法声明 ...

  10. [DP]硬币问题

    今天再写一下硬币问题 为什么是再呢 这是个很羞耻的话题 昨天写了一遍硬币 在某谷上跑 没错 挂掉了 TLE MD_SB ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ...